Completed
Pull Request — master (#7057)
by Blizzz
13:29
created
apps/user_ldap/composer/composer/autoload_classmap.php 1 patch
Spacing   +51 added lines, -51 removed lines patch added patch discarded remove patch
@@ -6,55 +6,55 @@
 block discarded – undo
6 6
 $baseDir = $vendorDir;
7 7
 
8 8
 return array(
9
-    'OCA\\User_LDAP\\Access' => $baseDir . '/../lib/Access.php',
10
-    'OCA\\User_LDAP\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php',
11
-    'OCA\\User_LDAP\\BackendUtility' => $baseDir . '/../lib/BackendUtility.php',
12
-    'OCA\\User_LDAP\\Command\\CheckUser' => $baseDir . '/../lib/Command/CheckUser.php',
13
-    'OCA\\User_LDAP\\Command\\CreateEmptyConfig' => $baseDir . '/../lib/Command/CreateEmptyConfig.php',
14
-    'OCA\\User_LDAP\\Command\\DeleteConfig' => $baseDir . '/../lib/Command/DeleteConfig.php',
15
-    'OCA\\User_LDAP\\Command\\Search' => $baseDir . '/../lib/Command/Search.php',
16
-    'OCA\\User_LDAP\\Command\\SetConfig' => $baseDir . '/../lib/Command/SetConfig.php',
17
-    'OCA\\User_LDAP\\Command\\ShowConfig' => $baseDir . '/../lib/Command/ShowConfig.php',
18
-    'OCA\\User_LDAP\\Command\\ShowRemnants' => $baseDir . '/../lib/Command/ShowRemnants.php',
19
-    'OCA\\User_LDAP\\Command\\TestConfig' => $baseDir . '/../lib/Command/TestConfig.php',
20
-    'OCA\\User_LDAP\\Configuration' => $baseDir . '/../lib/Configuration.php',
21
-    'OCA\\User_LDAP\\Connection' => $baseDir . '/../lib/Connection.php',
22
-    'OCA\\User_LDAP\\Controller\\ConfigAPIController' => $baseDir . '/../lib/Controller/ConfigAPIController.php',
23
-    'OCA\\User_LDAP\\Controller\\RenewPasswordController' => $baseDir . '/../lib/Controller/RenewPasswordController.php',
24
-    'OCA\\User_LDAP\\Exceptions\\ConstraintViolationException' => $baseDir . '/../lib/Exceptions/ConstraintViolationException.php',
25
-    'OCA\\User_LDAP\\Exceptions\\NotOnLDAP' => $baseDir . '/../lib/Exceptions/NotOnLDAP.php',
26
-    'OCA\\User_LDAP\\FilesystemHelper' => $baseDir . '/../lib/FilesystemHelper.php',
27
-    'OCA\\User_LDAP\\Group_LDAP' => $baseDir . '/../lib/Group_LDAP.php',
28
-    'OCA\\User_LDAP\\Group_Proxy' => $baseDir . '/../lib/Group_Proxy.php',
29
-    'OCA\\User_LDAP\\Helper' => $baseDir . '/../lib/Helper.php',
30
-    'OCA\\User_LDAP\\ILDAPWrapper' => $baseDir . '/../lib/ILDAPWrapper.php',
31
-    'OCA\\User_LDAP\\IUserLDAP' => $baseDir . '/../lib/IUserLDAP.php',
32
-    'OCA\\User_LDAP\\Jobs\\CleanUp' => $baseDir . '/../lib/Jobs/CleanUp.php',
33
-    'OCA\\User_LDAP\\Jobs\\Sync' => $baseDir . '/../lib/Jobs/Sync.php',
34
-    'OCA\\User_LDAP\\Jobs\\UpdateGroups' => $baseDir . '/../lib/Jobs/UpdateGroups.php',
35
-    'OCA\\User_LDAP\\LDAP' => $baseDir . '/../lib/LDAP.php',
36
-    'OCA\\User_LDAP\\LDAPProvider' => $baseDir . '/../lib/LDAPProvider.php',
37
-    'OCA\\User_LDAP\\LDAPProviderFactory' => $baseDir . '/../lib/LDAPProviderFactory.php',
38
-    'OCA\\User_LDAP\\LDAPUtility' => $baseDir . '/../lib/LDAPUtility.php',
39
-    'OCA\\User_LDAP\\LogWrapper' => $baseDir . '/../lib/LogWrapper.php',
40
-    'OCA\\User_LDAP\\Mapping\\AbstractMapping' => $baseDir . '/../lib/Mapping/AbstractMapping.php',
41
-    'OCA\\User_LDAP\\Mapping\\GroupMapping' => $baseDir . '/../lib/Mapping/GroupMapping.php',
42
-    'OCA\\User_LDAP\\Mapping\\UserMapping' => $baseDir . '/../lib/Mapping/UserMapping.php',
43
-    'OCA\\User_LDAP\\Migration\\UUIDFix' => $baseDir . '/../lib/Migration/UUIDFix.php',
44
-    'OCA\\User_LDAP\\Migration\\UUIDFixGroup' => $baseDir . '/../lib/Migration/UUIDFixGroup.php',
45
-    'OCA\\User_LDAP\\Migration\\UUIDFixInsert' => $baseDir . '/../lib/Migration/UUIDFixInsert.php',
46
-    'OCA\\User_LDAP\\Migration\\UUIDFixUser' => $baseDir . '/../lib/Migration/UUIDFixUser.php',
47
-    'OCA\\User_LDAP\\Notification\\Notifier' => $baseDir . '/../lib/Notification/Notifier.php',
48
-    'OCA\\User_LDAP\\Proxy' => $baseDir . '/../lib/Proxy.php',
49
-    'OCA\\User_LDAP\\Settings\\Admin' => $baseDir . '/../lib/Settings/Admin.php',
50
-    'OCA\\User_LDAP\\Settings\\Section' => $baseDir . '/../lib/Settings/Section.php',
51
-    'OCA\\User_LDAP\\User\\DeletedUsersIndex' => $baseDir . '/../lib/User/DeletedUsersIndex.php',
52
-    'OCA\\User_LDAP\\User\\IUserTools' => $baseDir . '/../lib/User/IUserTools.php',
53
-    'OCA\\User_LDAP\\User\\Manager' => $baseDir . '/../lib/User/Manager.php',
54
-    'OCA\\User_LDAP\\User\\OfflineUser' => $baseDir . '/../lib/User/OfflineUser.php',
55
-    'OCA\\User_LDAP\\User\\User' => $baseDir . '/../lib/User/User.php',
56
-    'OCA\\User_LDAP\\User_LDAP' => $baseDir . '/../lib/User_LDAP.php',
57
-    'OCA\\User_LDAP\\User_Proxy' => $baseDir . '/../lib/User_Proxy.php',
58
-    'OCA\\User_LDAP\\Wizard' => $baseDir . '/../lib/Wizard.php',
59
-    'OCA\\User_LDAP\\WizardResult' => $baseDir . '/../lib/WizardResult.php',
9
+    'OCA\\User_LDAP\\Access' => $baseDir.'/../lib/Access.php',
10
+    'OCA\\User_LDAP\\AppInfo\\Application' => $baseDir.'/../lib/AppInfo/Application.php',
11
+    'OCA\\User_LDAP\\BackendUtility' => $baseDir.'/../lib/BackendUtility.php',
12
+    'OCA\\User_LDAP\\Command\\CheckUser' => $baseDir.'/../lib/Command/CheckUser.php',
13
+    'OCA\\User_LDAP\\Command\\CreateEmptyConfig' => $baseDir.'/../lib/Command/CreateEmptyConfig.php',
14
+    'OCA\\User_LDAP\\Command\\DeleteConfig' => $baseDir.'/../lib/Command/DeleteConfig.php',
15
+    'OCA\\User_LDAP\\Command\\Search' => $baseDir.'/../lib/Command/Search.php',
16
+    'OCA\\User_LDAP\\Command\\SetConfig' => $baseDir.'/../lib/Command/SetConfig.php',
17
+    'OCA\\User_LDAP\\Command\\ShowConfig' => $baseDir.'/../lib/Command/ShowConfig.php',
18
+    'OCA\\User_LDAP\\Command\\ShowRemnants' => $baseDir.'/../lib/Command/ShowRemnants.php',
19
+    'OCA\\User_LDAP\\Command\\TestConfig' => $baseDir.'/../lib/Command/TestConfig.php',
20
+    'OCA\\User_LDAP\\Configuration' => $baseDir.'/../lib/Configuration.php',
21
+    'OCA\\User_LDAP\\Connection' => $baseDir.'/../lib/Connection.php',
22
+    'OCA\\User_LDAP\\Controller\\ConfigAPIController' => $baseDir.'/../lib/Controller/ConfigAPIController.php',
23
+    'OCA\\User_LDAP\\Controller\\RenewPasswordController' => $baseDir.'/../lib/Controller/RenewPasswordController.php',
24
+    'OCA\\User_LDAP\\Exceptions\\ConstraintViolationException' => $baseDir.'/../lib/Exceptions/ConstraintViolationException.php',
25
+    'OCA\\User_LDAP\\Exceptions\\NotOnLDAP' => $baseDir.'/../lib/Exceptions/NotOnLDAP.php',
26
+    'OCA\\User_LDAP\\FilesystemHelper' => $baseDir.'/../lib/FilesystemHelper.php',
27
+    'OCA\\User_LDAP\\Group_LDAP' => $baseDir.'/../lib/Group_LDAP.php',
28
+    'OCA\\User_LDAP\\Group_Proxy' => $baseDir.'/../lib/Group_Proxy.php',
29
+    'OCA\\User_LDAP\\Helper' => $baseDir.'/../lib/Helper.php',
30
+    'OCA\\User_LDAP\\ILDAPWrapper' => $baseDir.'/../lib/ILDAPWrapper.php',
31
+    'OCA\\User_LDAP\\IUserLDAP' => $baseDir.'/../lib/IUserLDAP.php',
32
+    'OCA\\User_LDAP\\Jobs\\CleanUp' => $baseDir.'/../lib/Jobs/CleanUp.php',
33
+    'OCA\\User_LDAP\\Jobs\\Sync' => $baseDir.'/../lib/Jobs/Sync.php',
34
+    'OCA\\User_LDAP\\Jobs\\UpdateGroups' => $baseDir.'/../lib/Jobs/UpdateGroups.php',
35
+    'OCA\\User_LDAP\\LDAP' => $baseDir.'/../lib/LDAP.php',
36
+    'OCA\\User_LDAP\\LDAPProvider' => $baseDir.'/../lib/LDAPProvider.php',
37
+    'OCA\\User_LDAP\\LDAPProviderFactory' => $baseDir.'/../lib/LDAPProviderFactory.php',
38
+    'OCA\\User_LDAP\\LDAPUtility' => $baseDir.'/../lib/LDAPUtility.php',
39
+    'OCA\\User_LDAP\\LogWrapper' => $baseDir.'/../lib/LogWrapper.php',
40
+    'OCA\\User_LDAP\\Mapping\\AbstractMapping' => $baseDir.'/../lib/Mapping/AbstractMapping.php',
41
+    'OCA\\User_LDAP\\Mapping\\GroupMapping' => $baseDir.'/../lib/Mapping/GroupMapping.php',
42
+    'OCA\\User_LDAP\\Mapping\\UserMapping' => $baseDir.'/../lib/Mapping/UserMapping.php',
43
+    'OCA\\User_LDAP\\Migration\\UUIDFix' => $baseDir.'/../lib/Migration/UUIDFix.php',
44
+    'OCA\\User_LDAP\\Migration\\UUIDFixGroup' => $baseDir.'/../lib/Migration/UUIDFixGroup.php',
45
+    'OCA\\User_LDAP\\Migration\\UUIDFixInsert' => $baseDir.'/../lib/Migration/UUIDFixInsert.php',
46
+    'OCA\\User_LDAP\\Migration\\UUIDFixUser' => $baseDir.'/../lib/Migration/UUIDFixUser.php',
47
+    'OCA\\User_LDAP\\Notification\\Notifier' => $baseDir.'/../lib/Notification/Notifier.php',
48
+    'OCA\\User_LDAP\\Proxy' => $baseDir.'/../lib/Proxy.php',
49
+    'OCA\\User_LDAP\\Settings\\Admin' => $baseDir.'/../lib/Settings/Admin.php',
50
+    'OCA\\User_LDAP\\Settings\\Section' => $baseDir.'/../lib/Settings/Section.php',
51
+    'OCA\\User_LDAP\\User\\DeletedUsersIndex' => $baseDir.'/../lib/User/DeletedUsersIndex.php',
52
+    'OCA\\User_LDAP\\User\\IUserTools' => $baseDir.'/../lib/User/IUserTools.php',
53
+    'OCA\\User_LDAP\\User\\Manager' => $baseDir.'/../lib/User/Manager.php',
54
+    'OCA\\User_LDAP\\User\\OfflineUser' => $baseDir.'/../lib/User/OfflineUser.php',
55
+    'OCA\\User_LDAP\\User\\User' => $baseDir.'/../lib/User/User.php',
56
+    'OCA\\User_LDAP\\User_LDAP' => $baseDir.'/../lib/User_LDAP.php',
57
+    'OCA\\User_LDAP\\User_Proxy' => $baseDir.'/../lib/User_Proxy.php',
58
+    'OCA\\User_LDAP\\Wizard' => $baseDir.'/../lib/Wizard.php',
59
+    'OCA\\User_LDAP\\WizardResult' => $baseDir.'/../lib/WizardResult.php',
60 60
 );
Please login to merge, or discard this patch.
apps/user_ldap/composer/composer/autoload_static.php 1 patch
Spacing   +58 added lines, -58 removed lines patch added patch discarded remove patch
@@ -6,77 +6,77 @@
 block discarded – undo
6 6
 
7 7
 class ComposerStaticInitUser_LDAP
8 8
 {
9
-    public static $prefixLengthsPsr4 = array (
9
+    public static $prefixLengthsPsr4 = array(
10 10
         'O' => 
11
-        array (
11
+        array(
12 12
             'OCA\\User_LDAP\\' => 14,
13 13
         ),
14 14
     );
15 15
 
16
-    public static $prefixDirsPsr4 = array (
16
+    public static $prefixDirsPsr4 = array(
17 17
         'OCA\\User_LDAP\\' => 
18
-        array (
19
-            0 => __DIR__ . '/..' . '/../lib',
18
+        array(
19
+            0 => __DIR__.'/..'.'/../lib',
20 20
         ),
21 21
     );
22 22
 
23
-    public static $classMap = array (
24
-        'OCA\\User_LDAP\\Access' => __DIR__ . '/..' . '/../lib/Access.php',
25
-        'OCA\\User_LDAP\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php',
26
-        'OCA\\User_LDAP\\BackendUtility' => __DIR__ . '/..' . '/../lib/BackendUtility.php',
27
-        'OCA\\User_LDAP\\Command\\CheckUser' => __DIR__ . '/..' . '/../lib/Command/CheckUser.php',
28
-        'OCA\\User_LDAP\\Command\\CreateEmptyConfig' => __DIR__ . '/..' . '/../lib/Command/CreateEmptyConfig.php',
29
-        'OCA\\User_LDAP\\Command\\DeleteConfig' => __DIR__ . '/..' . '/../lib/Command/DeleteConfig.php',
30
-        'OCA\\User_LDAP\\Command\\Search' => __DIR__ . '/..' . '/../lib/Command/Search.php',
31
-        'OCA\\User_LDAP\\Command\\SetConfig' => __DIR__ . '/..' . '/../lib/Command/SetConfig.php',
32
-        'OCA\\User_LDAP\\Command\\ShowConfig' => __DIR__ . '/..' . '/../lib/Command/ShowConfig.php',
33
-        'OCA\\User_LDAP\\Command\\ShowRemnants' => __DIR__ . '/..' . '/../lib/Command/ShowRemnants.php',
34
-        'OCA\\User_LDAP\\Command\\TestConfig' => __DIR__ . '/..' . '/../lib/Command/TestConfig.php',
35
-        'OCA\\User_LDAP\\Configuration' => __DIR__ . '/..' . '/../lib/Configuration.php',
36
-        'OCA\\User_LDAP\\Connection' => __DIR__ . '/..' . '/../lib/Connection.php',
37
-        'OCA\\User_LDAP\\Controller\\ConfigAPIController' => __DIR__ . '/..' . '/../lib/Controller/ConfigAPIController.php',
38
-        'OCA\\User_LDAP\\Controller\\RenewPasswordController' => __DIR__ . '/..' . '/../lib/Controller/RenewPasswordController.php',
39
-        'OCA\\User_LDAP\\Exceptions\\ConstraintViolationException' => __DIR__ . '/..' . '/../lib/Exceptions/ConstraintViolationException.php',
40
-        'OCA\\User_LDAP\\Exceptions\\NotOnLDAP' => __DIR__ . '/..' . '/../lib/Exceptions/NotOnLDAP.php',
41
-        'OCA\\User_LDAP\\FilesystemHelper' => __DIR__ . '/..' . '/../lib/FilesystemHelper.php',
42
-        'OCA\\User_LDAP\\Group_LDAP' => __DIR__ . '/..' . '/../lib/Group_LDAP.php',
43
-        'OCA\\User_LDAP\\Group_Proxy' => __DIR__ . '/..' . '/../lib/Group_Proxy.php',
44
-        'OCA\\User_LDAP\\Helper' => __DIR__ . '/..' . '/../lib/Helper.php',
45
-        'OCA\\User_LDAP\\ILDAPWrapper' => __DIR__ . '/..' . '/../lib/ILDAPWrapper.php',
46
-        'OCA\\User_LDAP\\IUserLDAP' => __DIR__ . '/..' . '/../lib/IUserLDAP.php',
47
-        'OCA\\User_LDAP\\Jobs\\CleanUp' => __DIR__ . '/..' . '/../lib/Jobs/CleanUp.php',
48
-        'OCA\\User_LDAP\\Jobs\\Sync' => __DIR__ . '/..' . '/../lib/Jobs/Sync.php',
49
-        'OCA\\User_LDAP\\Jobs\\UpdateGroups' => __DIR__ . '/..' . '/../lib/Jobs/UpdateGroups.php',
50
-        'OCA\\User_LDAP\\LDAP' => __DIR__ . '/..' . '/../lib/LDAP.php',
51
-        'OCA\\User_LDAP\\LDAPProvider' => __DIR__ . '/..' . '/../lib/LDAPProvider.php',
52
-        'OCA\\User_LDAP\\LDAPProviderFactory' => __DIR__ . '/..' . '/../lib/LDAPProviderFactory.php',
53
-        'OCA\\User_LDAP\\LDAPUtility' => __DIR__ . '/..' . '/../lib/LDAPUtility.php',
54
-        'OCA\\User_LDAP\\LogWrapper' => __DIR__ . '/..' . '/../lib/LogWrapper.php',
55
-        'OCA\\User_LDAP\\Mapping\\AbstractMapping' => __DIR__ . '/..' . '/../lib/Mapping/AbstractMapping.php',
56
-        'OCA\\User_LDAP\\Mapping\\GroupMapping' => __DIR__ . '/..' . '/../lib/Mapping/GroupMapping.php',
57
-        'OCA\\User_LDAP\\Mapping\\UserMapping' => __DIR__ . '/..' . '/../lib/Mapping/UserMapping.php',
58
-        'OCA\\User_LDAP\\Migration\\UUIDFix' => __DIR__ . '/..' . '/../lib/Migration/UUIDFix.php',
59
-        'OCA\\User_LDAP\\Migration\\UUIDFixGroup' => __DIR__ . '/..' . '/../lib/Migration/UUIDFixGroup.php',
60
-        'OCA\\User_LDAP\\Migration\\UUIDFixInsert' => __DIR__ . '/..' . '/../lib/Migration/UUIDFixInsert.php',
61
-        'OCA\\User_LDAP\\Migration\\UUIDFixUser' => __DIR__ . '/..' . '/../lib/Migration/UUIDFixUser.php',
62
-        'OCA\\User_LDAP\\Notification\\Notifier' => __DIR__ . '/..' . '/../lib/Notification/Notifier.php',
63
-        'OCA\\User_LDAP\\Proxy' => __DIR__ . '/..' . '/../lib/Proxy.php',
64
-        'OCA\\User_LDAP\\Settings\\Admin' => __DIR__ . '/..' . '/../lib/Settings/Admin.php',
65
-        'OCA\\User_LDAP\\Settings\\Section' => __DIR__ . '/..' . '/../lib/Settings/Section.php',
66
-        'OCA\\User_LDAP\\User\\DeletedUsersIndex' => __DIR__ . '/..' . '/../lib/User/DeletedUsersIndex.php',
67
-        'OCA\\User_LDAP\\User\\IUserTools' => __DIR__ . '/..' . '/../lib/User/IUserTools.php',
68
-        'OCA\\User_LDAP\\User\\Manager' => __DIR__ . '/..' . '/../lib/User/Manager.php',
69
-        'OCA\\User_LDAP\\User\\OfflineUser' => __DIR__ . '/..' . '/../lib/User/OfflineUser.php',
70
-        'OCA\\User_LDAP\\User\\User' => __DIR__ . '/..' . '/../lib/User/User.php',
71
-        'OCA\\User_LDAP\\User_LDAP' => __DIR__ . '/..' . '/../lib/User_LDAP.php',
72
-        'OCA\\User_LDAP\\User_Proxy' => __DIR__ . '/..' . '/../lib/User_Proxy.php',
73
-        'OCA\\User_LDAP\\Wizard' => __DIR__ . '/..' . '/../lib/Wizard.php',
74
-        'OCA\\User_LDAP\\WizardResult' => __DIR__ . '/..' . '/../lib/WizardResult.php',
23
+    public static $classMap = array(
24
+        'OCA\\User_LDAP\\Access' => __DIR__.'/..'.'/../lib/Access.php',
25
+        'OCA\\User_LDAP\\AppInfo\\Application' => __DIR__.'/..'.'/../lib/AppInfo/Application.php',
26
+        'OCA\\User_LDAP\\BackendUtility' => __DIR__.'/..'.'/../lib/BackendUtility.php',
27
+        'OCA\\User_LDAP\\Command\\CheckUser' => __DIR__.'/..'.'/../lib/Command/CheckUser.php',
28
+        'OCA\\User_LDAP\\Command\\CreateEmptyConfig' => __DIR__.'/..'.'/../lib/Command/CreateEmptyConfig.php',
29
+        'OCA\\User_LDAP\\Command\\DeleteConfig' => __DIR__.'/..'.'/../lib/Command/DeleteConfig.php',
30
+        'OCA\\User_LDAP\\Command\\Search' => __DIR__.'/..'.'/../lib/Command/Search.php',
31
+        'OCA\\User_LDAP\\Command\\SetConfig' => __DIR__.'/..'.'/../lib/Command/SetConfig.php',
32
+        'OCA\\User_LDAP\\Command\\ShowConfig' => __DIR__.'/..'.'/../lib/Command/ShowConfig.php',
33
+        'OCA\\User_LDAP\\Command\\ShowRemnants' => __DIR__.'/..'.'/../lib/Command/ShowRemnants.php',
34
+        'OCA\\User_LDAP\\Command\\TestConfig' => __DIR__.'/..'.'/../lib/Command/TestConfig.php',
35
+        'OCA\\User_LDAP\\Configuration' => __DIR__.'/..'.'/../lib/Configuration.php',
36
+        'OCA\\User_LDAP\\Connection' => __DIR__.'/..'.'/../lib/Connection.php',
37
+        'OCA\\User_LDAP\\Controller\\ConfigAPIController' => __DIR__.'/..'.'/../lib/Controller/ConfigAPIController.php',
38
+        'OCA\\User_LDAP\\Controller\\RenewPasswordController' => __DIR__.'/..'.'/../lib/Controller/RenewPasswordController.php',
39
+        'OCA\\User_LDAP\\Exceptions\\ConstraintViolationException' => __DIR__.'/..'.'/../lib/Exceptions/ConstraintViolationException.php',
40
+        'OCA\\User_LDAP\\Exceptions\\NotOnLDAP' => __DIR__.'/..'.'/../lib/Exceptions/NotOnLDAP.php',
41
+        'OCA\\User_LDAP\\FilesystemHelper' => __DIR__.'/..'.'/../lib/FilesystemHelper.php',
42
+        'OCA\\User_LDAP\\Group_LDAP' => __DIR__.'/..'.'/../lib/Group_LDAP.php',
43
+        'OCA\\User_LDAP\\Group_Proxy' => __DIR__.'/..'.'/../lib/Group_Proxy.php',
44
+        'OCA\\User_LDAP\\Helper' => __DIR__.'/..'.'/../lib/Helper.php',
45
+        'OCA\\User_LDAP\\ILDAPWrapper' => __DIR__.'/..'.'/../lib/ILDAPWrapper.php',
46
+        'OCA\\User_LDAP\\IUserLDAP' => __DIR__.'/..'.'/../lib/IUserLDAP.php',
47
+        'OCA\\User_LDAP\\Jobs\\CleanUp' => __DIR__.'/..'.'/../lib/Jobs/CleanUp.php',
48
+        'OCA\\User_LDAP\\Jobs\\Sync' => __DIR__.'/..'.'/../lib/Jobs/Sync.php',
49
+        'OCA\\User_LDAP\\Jobs\\UpdateGroups' => __DIR__.'/..'.'/../lib/Jobs/UpdateGroups.php',
50
+        'OCA\\User_LDAP\\LDAP' => __DIR__.'/..'.'/../lib/LDAP.php',
51
+        'OCA\\User_LDAP\\LDAPProvider' => __DIR__.'/..'.'/../lib/LDAPProvider.php',
52
+        'OCA\\User_LDAP\\LDAPProviderFactory' => __DIR__.'/..'.'/../lib/LDAPProviderFactory.php',
53
+        'OCA\\User_LDAP\\LDAPUtility' => __DIR__.'/..'.'/../lib/LDAPUtility.php',
54
+        'OCA\\User_LDAP\\LogWrapper' => __DIR__.'/..'.'/../lib/LogWrapper.php',
55
+        'OCA\\User_LDAP\\Mapping\\AbstractMapping' => __DIR__.'/..'.'/../lib/Mapping/AbstractMapping.php',
56
+        'OCA\\User_LDAP\\Mapping\\GroupMapping' => __DIR__.'/..'.'/../lib/Mapping/GroupMapping.php',
57
+        'OCA\\User_LDAP\\Mapping\\UserMapping' => __DIR__.'/..'.'/../lib/Mapping/UserMapping.php',
58
+        'OCA\\User_LDAP\\Migration\\UUIDFix' => __DIR__.'/..'.'/../lib/Migration/UUIDFix.php',
59
+        'OCA\\User_LDAP\\Migration\\UUIDFixGroup' => __DIR__.'/..'.'/../lib/Migration/UUIDFixGroup.php',
60
+        'OCA\\User_LDAP\\Migration\\UUIDFixInsert' => __DIR__.'/..'.'/../lib/Migration/UUIDFixInsert.php',
61
+        'OCA\\User_LDAP\\Migration\\UUIDFixUser' => __DIR__.'/..'.'/../lib/Migration/UUIDFixUser.php',
62
+        'OCA\\User_LDAP\\Notification\\Notifier' => __DIR__.'/..'.'/../lib/Notification/Notifier.php',
63
+        'OCA\\User_LDAP\\Proxy' => __DIR__.'/..'.'/../lib/Proxy.php',
64
+        'OCA\\User_LDAP\\Settings\\Admin' => __DIR__.'/..'.'/../lib/Settings/Admin.php',
65
+        'OCA\\User_LDAP\\Settings\\Section' => __DIR__.'/..'.'/../lib/Settings/Section.php',
66
+        'OCA\\User_LDAP\\User\\DeletedUsersIndex' => __DIR__.'/..'.'/../lib/User/DeletedUsersIndex.php',
67
+        'OCA\\User_LDAP\\User\\IUserTools' => __DIR__.'/..'.'/../lib/User/IUserTools.php',
68
+        'OCA\\User_LDAP\\User\\Manager' => __DIR__.'/..'.'/../lib/User/Manager.php',
69
+        'OCA\\User_LDAP\\User\\OfflineUser' => __DIR__.'/..'.'/../lib/User/OfflineUser.php',
70
+        'OCA\\User_LDAP\\User\\User' => __DIR__.'/..'.'/../lib/User/User.php',
71
+        'OCA\\User_LDAP\\User_LDAP' => __DIR__.'/..'.'/../lib/User_LDAP.php',
72
+        'OCA\\User_LDAP\\User_Proxy' => __DIR__.'/..'.'/../lib/User_Proxy.php',
73
+        'OCA\\User_LDAP\\Wizard' => __DIR__.'/..'.'/../lib/Wizard.php',
74
+        'OCA\\User_LDAP\\WizardResult' => __DIR__.'/..'.'/../lib/WizardResult.php',
75 75
     );
76 76
 
77 77
     public static function getInitializer(ClassLoader $loader)
78 78
     {
79
-        return \Closure::bind(function () use ($loader) {
79
+        return \Closure::bind(function() use ($loader) {
80 80
             $loader->prefixLengthsPsr4 = ComposerStaticInitUser_LDAP::$prefixLengthsPsr4;
81 81
             $loader->prefixDirsPsr4 = ComposerStaticInitUser_LDAP::$prefixDirsPsr4;
82 82
             $loader->classMap = ComposerStaticInitUser_LDAP::$classMap;
Please login to merge, or discard this patch.
apps/user_ldap/lib/Access.php 4 patches
Doc Comments   +7 added lines, -5 removed lines patch added patch discarded remove patch
@@ -500,7 +500,7 @@  discard block
 block discarded – undo
500 500
 
501 501
 	/**
502 502
 	 * returns the internal Nextcloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
503
-	 * @param string $dn the dn of the user object
503
+	 * @param string $fdn the dn of the user object
504 504
 	 * @param string $ldapName optional, the display name of the object
505 505
 	 * @return string|false with with the name to use in Nextcloud
506 506
 	 */
@@ -786,7 +786,7 @@  discard block
 block discarded – undo
786 786
 	 * the login filter.
787 787
 	 *
788 788
 	 * @param string $loginName
789
-	 * @param array $attributes optional, list of attributes to read
789
+	 * @param string[] $attributes optional, list of attributes to read
790 790
 	 * @return array
791 791
 	 */
792 792
 	public function fetchUsersByLoginName($loginName, $attributes = array('dn')) {
@@ -870,7 +870,7 @@  discard block
 block discarded – undo
870 870
 
871 871
 	/**
872 872
 	 * @param string $filter
873
-	 * @param string|string[] $attr
873
+	 * @param string[] $attr
874 874
 	 * @param int $limit
875 875
 	 * @param int $offset
876 876
 	 * @return array
@@ -918,7 +918,7 @@  discard block
 block discarded – undo
918 918
 
919 919
 	/**
920 920
 	 * @param string $filter
921
-	 * @param string|string[] $attr
921
+	 * @param string[] $attr
922 922
 	 * @param int $limit
923 923
 	 * @param int $offset
924 924
 	 * @return false|int
@@ -1018,6 +1018,7 @@  discard block
 block discarded – undo
1018 1018
 	 * retrieved. Results will according to the order in the array.
1019 1019
 	 * @param int $limit optional, maximum results to be counted
1020 1020
 	 * @param int $offset optional, a starting point
1021
+	 * @param string $filter
1021 1022
 	 * @return array|false array with the search result as first value and pagedSearchOK as
1022 1023
 	 * second | false if not successful
1023 1024
 	 * @throws \OC\ServerNotAvailableException
@@ -1274,7 +1275,7 @@  discard block
 block discarded – undo
1274 1275
 
1275 1276
 	/**
1276 1277
 	 * @param string $name
1277
-	 * @return bool|mixed|string
1278
+	 * @return string
1278 1279
 	 */
1279 1280
 	public function sanitizeUsername($name) {
1280 1281
 		if($this->connection->ldapIgnoreNamingRules) {
@@ -1298,6 +1299,7 @@  discard block
 block discarded – undo
1298 1299
 	* escapes (user provided) parts for LDAP filter
1299 1300
 	* @param string $input, the provided value
1300 1301
 	* @param bool $allowAsterisk whether in * at the beginning should be preserved
1302
+	* @param string $input
1301 1303
 	* @return string the escaped string
1302 1304
 	*/
1303 1305
 	public function escapeFilterPart($input, $allowAsterisk = false) {
Please login to merge, or discard this patch.
Unused Use Statements   -1 removed lines patch added patch discarded remove patch
@@ -46,7 +46,6 @@
 block discarded – undo
46 46
 use OCA\User_LDAP\User\Manager;
47 47
 use OCA\User_LDAP\User\OfflineUser;
48 48
 use OCA\User_LDAP\Mapping\AbstractMapping;
49
-
50 49
 use OC\ServerNotAvailableException;
51 50
 use OCP\IServerContainer;
52 51
 
Please login to merge, or discard this patch.
Indentation   +1859 added lines, -1859 removed lines patch added patch discarded remove patch
@@ -55,1620 +55,1620 @@  discard block
 block discarded – undo
55 55
  * @package OCA\User_LDAP
56 56
  */
57 57
 class Access extends LDAPUtility implements IUserTools {
58
-	const UUID_ATTRIBUTES = ['entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid'];
59
-
60
-	/** @var \OCA\User_LDAP\Connection */
61
-	public $connection;
62
-	/** @var Manager */
63
-	public $userManager;
64
-	//never ever check this var directly, always use getPagedSearchResultState
65
-	protected $pagedSearchedSuccessful;
66
-
67
-	/**
68
-	 * @var string[] $cookies an array of returned Paged Result cookies
69
-	 */
70
-	protected $cookies = array();
71
-
72
-	/**
73
-	 * @var string $lastCookie the last cookie returned from a Paged Results
74
-	 * operation, defaults to an empty string
75
-	 */
76
-	protected $lastCookie = '';
77
-
78
-	/**
79
-	 * @var AbstractMapping $userMapper
80
-	 */
81
-	protected $userMapper;
82
-
83
-	/**
84
-	* @var AbstractMapping $userMapper
85
-	*/
86
-	protected $groupMapper;
87
-
88
-	/**
89
-	 * @var \OCA\User_LDAP\Helper
90
-	 */
91
-	private $helper;
92
-	/** @var IServerContainer */
93
-	private $c;
94
-
95
-	public function __construct(
96
-		Connection $connection,
97
-		ILDAPWrapper $ldap,
98
-		Manager $userManager,
99
-		Helper $helper,
100
-		IServerContainer $c
101
-	) {
102
-		parent::__construct($ldap);
103
-		$this->connection = $connection;
104
-		$this->userManager = $userManager;
105
-		$this->userManager->setLdapAccess($this);
106
-		$this->helper = $helper;
107
-		$this->c = $c;
108
-	}
109
-
110
-	/**
111
-	 * sets the User Mapper
112
-	 * @param AbstractMapping $mapper
113
-	 */
114
-	public function setUserMapper(AbstractMapping $mapper) {
115
-		$this->userMapper = $mapper;
116
-	}
117
-
118
-	/**
119
-	 * returns the User Mapper
120
-	 * @throws \Exception
121
-	 * @return AbstractMapping
122
-	 */
123
-	public function getUserMapper() {
124
-		if(is_null($this->userMapper)) {
125
-			throw new \Exception('UserMapper was not assigned to this Access instance.');
126
-		}
127
-		return $this->userMapper;
128
-	}
129
-
130
-	/**
131
-	 * sets the Group Mapper
132
-	 * @param AbstractMapping $mapper
133
-	 */
134
-	public function setGroupMapper(AbstractMapping $mapper) {
135
-		$this->groupMapper = $mapper;
136
-	}
137
-
138
-	/**
139
-	 * returns the Group Mapper
140
-	 * @throws \Exception
141
-	 * @return AbstractMapping
142
-	 */
143
-	public function getGroupMapper() {
144
-		if(is_null($this->groupMapper)) {
145
-			throw new \Exception('GroupMapper was not assigned to this Access instance.');
146
-		}
147
-		return $this->groupMapper;
148
-	}
149
-
150
-	/**
151
-	 * @return bool
152
-	 */
153
-	private function checkConnection() {
154
-		return ($this->connection instanceof Connection);
155
-	}
156
-
157
-	/**
158
-	 * returns the Connection instance
159
-	 * @return \OCA\User_LDAP\Connection
160
-	 */
161
-	public function getConnection() {
162
-		return $this->connection;
163
-	}
164
-
165
-	/**
166
-	 * reads a given attribute for an LDAP record identified by a DN
167
-	 * @param string $dn the record in question
168
-	 * @param string $attr the attribute that shall be retrieved
169
-	 *        if empty, just check the record's existence
170
-	 * @param string $filter
171
-	 * @return array|false an array of values on success or an empty
172
-	 *          array if $attr is empty, false otherwise
173
-	 */
174
-	public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
175
-		if(!$this->checkConnection()) {
176
-			\OCP\Util::writeLog('user_ldap',
177
-				'No LDAP Connector assigned, access impossible for readAttribute.',
178
-				\OCP\Util::WARN);
179
-			return false;
180
-		}
181
-		$cr = $this->connection->getConnectionResource();
182
-		if(!$this->ldap->isResource($cr)) {
183
-			//LDAP not available
184
-			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
185
-			return false;
186
-		}
187
-		//Cancel possibly running Paged Results operation, otherwise we run in
188
-		//LDAP protocol errors
189
-		$this->abandonPagedSearch();
190
-		// openLDAP requires that we init a new Paged Search. Not needed by AD,
191
-		// but does not hurt either.
192
-		$pagingSize = intval($this->connection->ldapPagingSize);
193
-		// 0 won't result in replies, small numbers may leave out groups
194
-		// (cf. #12306), 500 is default for paging and should work everywhere.
195
-		$maxResults = $pagingSize > 20 ? $pagingSize : 500;
196
-		$attr = mb_strtolower($attr, 'UTF-8');
197
-		// the actual read attribute later may contain parameters on a ranged
198
-		// request, e.g. member;range=99-199. Depends on server reply.
199
-		$attrToRead = $attr;
200
-
201
-		$values = [];
202
-		$isRangeRequest = false;
203
-		do {
204
-			$result = $this->executeRead($cr, $dn, $attrToRead, $filter, $maxResults);
205
-			if(is_bool($result)) {
206
-				// when an exists request was run and it was successful, an empty
207
-				// array must be returned
208
-				return $result ? [] : false;
209
-			}
210
-
211
-			if (!$isRangeRequest) {
212
-				$values = $this->extractAttributeValuesFromResult($result, $attr);
213
-				if (!empty($values)) {
214
-					return $values;
215
-				}
216
-			}
217
-
218
-			$isRangeRequest = false;
219
-			$result = $this->extractRangeData($result, $attr);
220
-			if (!empty($result)) {
221
-				$normalizedResult = $this->extractAttributeValuesFromResult(
222
-					[ $attr => $result['values'] ],
223
-					$attr
224
-				);
225
-				$values = array_merge($values, $normalizedResult);
226
-
227
-				if($result['rangeHigh'] === '*') {
228
-					// when server replies with * as high range value, there are
229
-					// no more results left
230
-					return $values;
231
-				} else {
232
-					$low  = $result['rangeHigh'] + 1;
233
-					$attrToRead = $result['attributeName'] . ';range=' . $low . '-*';
234
-					$isRangeRequest = true;
235
-				}
236
-			}
237
-		} while($isRangeRequest);
238
-
239
-		\OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, \OCP\Util::DEBUG);
240
-		return false;
241
-	}
242
-
243
-	/**
244
-	 * Runs an read operation against LDAP
245
-	 *
246
-	 * @param resource $cr the LDAP connection
247
-	 * @param string $dn
248
-	 * @param string $attribute
249
-	 * @param string $filter
250
-	 * @param int $maxResults
251
-	 * @return array|bool false if there was any error, true if an exists check
252
-	 *                    was performed and the requested DN found, array with the
253
-	 *                    returned data on a successful usual operation
254
-	 */
255
-	public function executeRead($cr, $dn, $attribute, $filter, $maxResults) {
256
-		$this->initPagedSearch($filter, array($dn), array($attribute), $maxResults, 0);
257
-		$dn = $this->helper->DNasBaseParameter($dn);
258
-		$rr = @$this->invokeLDAPMethod('read', $cr, $dn, $filter, array($attribute));
259
-		if (!$this->ldap->isResource($rr)) {
260
-			if ($attribute !== '') {
261
-				//do not throw this message on userExists check, irritates
262
-				\OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN ' . $dn, \OCP\Util::DEBUG);
263
-			}
264
-			//in case an error occurs , e.g. object does not exist
265
-			return false;
266
-		}
267
-		if ($attribute === '' && ($filter === 'objectclass=*' || $this->invokeLDAPMethod('countEntries', $cr, $rr) === 1)) {
268
-			\OCP\Util::writeLog('user_ldap', 'readAttribute: ' . $dn . ' found', \OCP\Util::DEBUG);
269
-			return true;
270
-		}
271
-		$er = $this->invokeLDAPMethod('firstEntry', $cr, $rr);
272
-		if (!$this->ldap->isResource($er)) {
273
-			//did not match the filter, return false
274
-			return false;
275
-		}
276
-		//LDAP attributes are not case sensitive
277
-		$result = \OCP\Util::mb_array_change_key_case(
278
-			$this->invokeLDAPMethod('getAttributes', $cr, $er), MB_CASE_LOWER, 'UTF-8');
279
-
280
-		return $result;
281
-	}
282
-
283
-	/**
284
-	 * Normalizes a result grom getAttributes(), i.e. handles DNs and binary
285
-	 * data if present.
286
-	 *
287
-	 * @param array $result from ILDAPWrapper::getAttributes()
288
-	 * @param string $attribute the attribute name that was read
289
-	 * @return string[]
290
-	 */
291
-	public function extractAttributeValuesFromResult($result, $attribute) {
292
-		$values = [];
293
-		if(isset($result[$attribute]) && $result[$attribute]['count'] > 0) {
294
-			$lowercaseAttribute = strtolower($attribute);
295
-			for($i=0;$i<$result[$attribute]['count'];$i++) {
296
-				if($this->resemblesDN($attribute)) {
297
-					$values[] = $this->helper->sanitizeDN($result[$attribute][$i]);
298
-				} elseif($lowercaseAttribute === 'objectguid' || $lowercaseAttribute === 'guid') {
299
-					$values[] = $this->convertObjectGUID2Str($result[$attribute][$i]);
300
-				} else {
301
-					$values[] = $result[$attribute][$i];
302
-				}
303
-			}
304
-		}
305
-		return $values;
306
-	}
307
-
308
-	/**
309
-	 * Attempts to find ranged data in a getAttribute results and extracts the
310
-	 * returned values as well as information on the range and full attribute
311
-	 * name for further processing.
312
-	 *
313
-	 * @param array $result from ILDAPWrapper::getAttributes()
314
-	 * @param string $attribute the attribute name that was read. Without ";range=…"
315
-	 * @return array If a range was detected with keys 'values', 'attributeName',
316
-	 *               'attributeFull' and 'rangeHigh', otherwise empty.
317
-	 */
318
-	public function extractRangeData($result, $attribute) {
319
-		$keys = array_keys($result);
320
-		foreach($keys as $key) {
321
-			if($key !== $attribute && strpos($key, $attribute) === 0) {
322
-				$queryData = explode(';', $key);
323
-				if(strpos($queryData[1], 'range=') === 0) {
324
-					$high = substr($queryData[1], 1 + strpos($queryData[1], '-'));
325
-					$data = [
326
-						'values' => $result[$key],
327
-						'attributeName' => $queryData[0],
328
-						'attributeFull' => $key,
329
-						'rangeHigh' => $high,
330
-					];
331
-					return $data;
332
-				}
333
-			}
334
-		}
335
-		return [];
336
-	}
58
+    const UUID_ATTRIBUTES = ['entryuuid', 'nsuniqueid', 'objectguid', 'guid', 'ipauniqueid'];
59
+
60
+    /** @var \OCA\User_LDAP\Connection */
61
+    public $connection;
62
+    /** @var Manager */
63
+    public $userManager;
64
+    //never ever check this var directly, always use getPagedSearchResultState
65
+    protected $pagedSearchedSuccessful;
66
+
67
+    /**
68
+     * @var string[] $cookies an array of returned Paged Result cookies
69
+     */
70
+    protected $cookies = array();
71
+
72
+    /**
73
+     * @var string $lastCookie the last cookie returned from a Paged Results
74
+     * operation, defaults to an empty string
75
+     */
76
+    protected $lastCookie = '';
77
+
78
+    /**
79
+     * @var AbstractMapping $userMapper
80
+     */
81
+    protected $userMapper;
82
+
83
+    /**
84
+     * @var AbstractMapping $userMapper
85
+     */
86
+    protected $groupMapper;
87
+
88
+    /**
89
+     * @var \OCA\User_LDAP\Helper
90
+     */
91
+    private $helper;
92
+    /** @var IServerContainer */
93
+    private $c;
94
+
95
+    public function __construct(
96
+        Connection $connection,
97
+        ILDAPWrapper $ldap,
98
+        Manager $userManager,
99
+        Helper $helper,
100
+        IServerContainer $c
101
+    ) {
102
+        parent::__construct($ldap);
103
+        $this->connection = $connection;
104
+        $this->userManager = $userManager;
105
+        $this->userManager->setLdapAccess($this);
106
+        $this->helper = $helper;
107
+        $this->c = $c;
108
+    }
109
+
110
+    /**
111
+     * sets the User Mapper
112
+     * @param AbstractMapping $mapper
113
+     */
114
+    public function setUserMapper(AbstractMapping $mapper) {
115
+        $this->userMapper = $mapper;
116
+    }
117
+
118
+    /**
119
+     * returns the User Mapper
120
+     * @throws \Exception
121
+     * @return AbstractMapping
122
+     */
123
+    public function getUserMapper() {
124
+        if(is_null($this->userMapper)) {
125
+            throw new \Exception('UserMapper was not assigned to this Access instance.');
126
+        }
127
+        return $this->userMapper;
128
+    }
129
+
130
+    /**
131
+     * sets the Group Mapper
132
+     * @param AbstractMapping $mapper
133
+     */
134
+    public function setGroupMapper(AbstractMapping $mapper) {
135
+        $this->groupMapper = $mapper;
136
+    }
137
+
138
+    /**
139
+     * returns the Group Mapper
140
+     * @throws \Exception
141
+     * @return AbstractMapping
142
+     */
143
+    public function getGroupMapper() {
144
+        if(is_null($this->groupMapper)) {
145
+            throw new \Exception('GroupMapper was not assigned to this Access instance.');
146
+        }
147
+        return $this->groupMapper;
148
+    }
149
+
150
+    /**
151
+     * @return bool
152
+     */
153
+    private function checkConnection() {
154
+        return ($this->connection instanceof Connection);
155
+    }
156
+
157
+    /**
158
+     * returns the Connection instance
159
+     * @return \OCA\User_LDAP\Connection
160
+     */
161
+    public function getConnection() {
162
+        return $this->connection;
163
+    }
164
+
165
+    /**
166
+     * reads a given attribute for an LDAP record identified by a DN
167
+     * @param string $dn the record in question
168
+     * @param string $attr the attribute that shall be retrieved
169
+     *        if empty, just check the record's existence
170
+     * @param string $filter
171
+     * @return array|false an array of values on success or an empty
172
+     *          array if $attr is empty, false otherwise
173
+     */
174
+    public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
175
+        if(!$this->checkConnection()) {
176
+            \OCP\Util::writeLog('user_ldap',
177
+                'No LDAP Connector assigned, access impossible for readAttribute.',
178
+                \OCP\Util::WARN);
179
+            return false;
180
+        }
181
+        $cr = $this->connection->getConnectionResource();
182
+        if(!$this->ldap->isResource($cr)) {
183
+            //LDAP not available
184
+            \OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
185
+            return false;
186
+        }
187
+        //Cancel possibly running Paged Results operation, otherwise we run in
188
+        //LDAP protocol errors
189
+        $this->abandonPagedSearch();
190
+        // openLDAP requires that we init a new Paged Search. Not needed by AD,
191
+        // but does not hurt either.
192
+        $pagingSize = intval($this->connection->ldapPagingSize);
193
+        // 0 won't result in replies, small numbers may leave out groups
194
+        // (cf. #12306), 500 is default for paging and should work everywhere.
195
+        $maxResults = $pagingSize > 20 ? $pagingSize : 500;
196
+        $attr = mb_strtolower($attr, 'UTF-8');
197
+        // the actual read attribute later may contain parameters on a ranged
198
+        // request, e.g. member;range=99-199. Depends on server reply.
199
+        $attrToRead = $attr;
200
+
201
+        $values = [];
202
+        $isRangeRequest = false;
203
+        do {
204
+            $result = $this->executeRead($cr, $dn, $attrToRead, $filter, $maxResults);
205
+            if(is_bool($result)) {
206
+                // when an exists request was run and it was successful, an empty
207
+                // array must be returned
208
+                return $result ? [] : false;
209
+            }
210
+
211
+            if (!$isRangeRequest) {
212
+                $values = $this->extractAttributeValuesFromResult($result, $attr);
213
+                if (!empty($values)) {
214
+                    return $values;
215
+                }
216
+            }
217
+
218
+            $isRangeRequest = false;
219
+            $result = $this->extractRangeData($result, $attr);
220
+            if (!empty($result)) {
221
+                $normalizedResult = $this->extractAttributeValuesFromResult(
222
+                    [ $attr => $result['values'] ],
223
+                    $attr
224
+                );
225
+                $values = array_merge($values, $normalizedResult);
226
+
227
+                if($result['rangeHigh'] === '*') {
228
+                    // when server replies with * as high range value, there are
229
+                    // no more results left
230
+                    return $values;
231
+                } else {
232
+                    $low  = $result['rangeHigh'] + 1;
233
+                    $attrToRead = $result['attributeName'] . ';range=' . $low . '-*';
234
+                    $isRangeRequest = true;
235
+                }
236
+            }
237
+        } while($isRangeRequest);
238
+
239
+        \OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, \OCP\Util::DEBUG);
240
+        return false;
241
+    }
242
+
243
+    /**
244
+     * Runs an read operation against LDAP
245
+     *
246
+     * @param resource $cr the LDAP connection
247
+     * @param string $dn
248
+     * @param string $attribute
249
+     * @param string $filter
250
+     * @param int $maxResults
251
+     * @return array|bool false if there was any error, true if an exists check
252
+     *                    was performed and the requested DN found, array with the
253
+     *                    returned data on a successful usual operation
254
+     */
255
+    public function executeRead($cr, $dn, $attribute, $filter, $maxResults) {
256
+        $this->initPagedSearch($filter, array($dn), array($attribute), $maxResults, 0);
257
+        $dn = $this->helper->DNasBaseParameter($dn);
258
+        $rr = @$this->invokeLDAPMethod('read', $cr, $dn, $filter, array($attribute));
259
+        if (!$this->ldap->isResource($rr)) {
260
+            if ($attribute !== '') {
261
+                //do not throw this message on userExists check, irritates
262
+                \OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN ' . $dn, \OCP\Util::DEBUG);
263
+            }
264
+            //in case an error occurs , e.g. object does not exist
265
+            return false;
266
+        }
267
+        if ($attribute === '' && ($filter === 'objectclass=*' || $this->invokeLDAPMethod('countEntries', $cr, $rr) === 1)) {
268
+            \OCP\Util::writeLog('user_ldap', 'readAttribute: ' . $dn . ' found', \OCP\Util::DEBUG);
269
+            return true;
270
+        }
271
+        $er = $this->invokeLDAPMethod('firstEntry', $cr, $rr);
272
+        if (!$this->ldap->isResource($er)) {
273
+            //did not match the filter, return false
274
+            return false;
275
+        }
276
+        //LDAP attributes are not case sensitive
277
+        $result = \OCP\Util::mb_array_change_key_case(
278
+            $this->invokeLDAPMethod('getAttributes', $cr, $er), MB_CASE_LOWER, 'UTF-8');
279
+
280
+        return $result;
281
+    }
282
+
283
+    /**
284
+     * Normalizes a result grom getAttributes(), i.e. handles DNs and binary
285
+     * data if present.
286
+     *
287
+     * @param array $result from ILDAPWrapper::getAttributes()
288
+     * @param string $attribute the attribute name that was read
289
+     * @return string[]
290
+     */
291
+    public function extractAttributeValuesFromResult($result, $attribute) {
292
+        $values = [];
293
+        if(isset($result[$attribute]) && $result[$attribute]['count'] > 0) {
294
+            $lowercaseAttribute = strtolower($attribute);
295
+            for($i=0;$i<$result[$attribute]['count'];$i++) {
296
+                if($this->resemblesDN($attribute)) {
297
+                    $values[] = $this->helper->sanitizeDN($result[$attribute][$i]);
298
+                } elseif($lowercaseAttribute === 'objectguid' || $lowercaseAttribute === 'guid') {
299
+                    $values[] = $this->convertObjectGUID2Str($result[$attribute][$i]);
300
+                } else {
301
+                    $values[] = $result[$attribute][$i];
302
+                }
303
+            }
304
+        }
305
+        return $values;
306
+    }
307
+
308
+    /**
309
+     * Attempts to find ranged data in a getAttribute results and extracts the
310
+     * returned values as well as information on the range and full attribute
311
+     * name for further processing.
312
+     *
313
+     * @param array $result from ILDAPWrapper::getAttributes()
314
+     * @param string $attribute the attribute name that was read. Without ";range=…"
315
+     * @return array If a range was detected with keys 'values', 'attributeName',
316
+     *               'attributeFull' and 'rangeHigh', otherwise empty.
317
+     */
318
+    public function extractRangeData($result, $attribute) {
319
+        $keys = array_keys($result);
320
+        foreach($keys as $key) {
321
+            if($key !== $attribute && strpos($key, $attribute) === 0) {
322
+                $queryData = explode(';', $key);
323
+                if(strpos($queryData[1], 'range=') === 0) {
324
+                    $high = substr($queryData[1], 1 + strpos($queryData[1], '-'));
325
+                    $data = [
326
+                        'values' => $result[$key],
327
+                        'attributeName' => $queryData[0],
328
+                        'attributeFull' => $key,
329
+                        'rangeHigh' => $high,
330
+                    ];
331
+                    return $data;
332
+                }
333
+            }
334
+        }
335
+        return [];
336
+    }
337 337
 	
338
-	/**
339
-	 * Set password for an LDAP user identified by a DN
340
-	 *
341
-	 * @param string $userDN the user in question
342
-	 * @param string $password the new password
343
-	 * @return bool
344
-	 * @throws HintException
345
-	 * @throws \Exception
346
-	 */
347
-	public function setPassword($userDN, $password) {
348
-		if(intval($this->connection->turnOnPasswordChange) !== 1) {
349
-			throw new \Exception('LDAP password changes are disabled.');
350
-		}
351
-		$cr = $this->connection->getConnectionResource();
352
-		if(!$this->ldap->isResource($cr)) {
353
-			//LDAP not available
354
-			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
355
-			return false;
356
-		}
357
-		try {
358
-			return @$this->invokeLDAPMethod('modReplace', $cr, $userDN, $password);
359
-		} catch(ConstraintViolationException $e) {
360
-			throw new HintException('Password change rejected.', \OC::$server->getL10N('user_ldap')->t('Password change rejected. Hint: ').$e->getMessage(), $e->getCode());
361
-		}
362
-	}
363
-
364
-	/**
365
-	 * checks whether the given attributes value is probably a DN
366
-	 * @param string $attr the attribute in question
367
-	 * @return boolean if so true, otherwise false
368
-	 */
369
-	private function resemblesDN($attr) {
370
-		$resemblingAttributes = array(
371
-			'dn',
372
-			'uniquemember',
373
-			'member',
374
-			// memberOf is an "operational" attribute, without a definition in any RFC
375
-			'memberof'
376
-		);
377
-		return in_array($attr, $resemblingAttributes);
378
-	}
379
-
380
-	/**
381
-	 * checks whether the given string is probably a DN
382
-	 * @param string $string
383
-	 * @return boolean
384
-	 */
385
-	public function stringResemblesDN($string) {
386
-		$r = $this->ldap->explodeDN($string, 0);
387
-		// if exploding a DN succeeds and does not end up in
388
-		// an empty array except for $r[count] being 0.
389
-		return (is_array($r) && count($r) > 1);
390
-	}
391
-
392
-	/**
393
-	 * returns a DN-string that is cleaned from not domain parts, e.g.
394
-	 * cn=foo,cn=bar,dc=foobar,dc=server,dc=org
395
-	 * becomes dc=foobar,dc=server,dc=org
396
-	 * @param string $dn
397
-	 * @return string
398
-	 */
399
-	public function getDomainDNFromDN($dn) {
400
-		$allParts = $this->ldap->explodeDN($dn, 0);
401
-		if($allParts === false) {
402
-			//not a valid DN
403
-			return '';
404
-		}
405
-		$domainParts = array();
406
-		$dcFound = false;
407
-		foreach($allParts as $part) {
408
-			if(!$dcFound && strpos($part, 'dc=') === 0) {
409
-				$dcFound = true;
410
-			}
411
-			if($dcFound) {
412
-				$domainParts[] = $part;
413
-			}
414
-		}
415
-		$domainDN = implode(',', $domainParts);
416
-		return $domainDN;
417
-	}
418
-
419
-	/**
420
-	 * returns the LDAP DN for the given internal Nextcloud name of the group
421
-	 * @param string $name the Nextcloud name in question
422
-	 * @return string|false LDAP DN on success, otherwise false
423
-	 */
424
-	public function groupname2dn($name) {
425
-		return $this->groupMapper->getDNByName($name);
426
-	}
427
-
428
-	/**
429
-	 * returns the LDAP DN for the given internal Nextcloud name of the user
430
-	 * @param string $name the Nextcloud name in question
431
-	 * @return string|false with the LDAP DN on success, otherwise false
432
-	 */
433
-	public function username2dn($name) {
434
-		$fdn = $this->userMapper->getDNByName($name);
435
-
436
-		//Check whether the DN belongs to the Base, to avoid issues on multi-
437
-		//server setups
438
-		if(is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
439
-			return $fdn;
440
-		}
441
-
442
-		return false;
443
-	}
444
-
445
-	/**
446
-	 * returns the internal Nextcloud name for the given LDAP DN of the group, false on DN outside of search DN or failure
447
-	 * @param string $fdn the dn of the group object
448
-	 * @param string $ldapName optional, the display name of the object
449
-	 * @return string|false with the name to use in Nextcloud, false on DN outside of search DN
450
-	 */
451
-	public function dn2groupname($fdn, $ldapName = null) {
452
-		//To avoid bypassing the base DN settings under certain circumstances
453
-		//with the group support, check whether the provided DN matches one of
454
-		//the given Bases
455
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
456
-			return false;
457
-		}
458
-
459
-		return $this->dn2ocname($fdn, $ldapName, false);
460
-	}
461
-
462
-	/**
463
-	 * accepts an array of group DNs and tests whether they match the user
464
-	 * filter by doing read operations against the group entries. Returns an
465
-	 * array of DNs that match the filter.
466
-	 *
467
-	 * @param string[] $groupDNs
468
-	 * @return string[]
469
-	 */
470
-	public function groupsMatchFilter($groupDNs) {
471
-		$validGroupDNs = [];
472
-		foreach($groupDNs as $dn) {
473
-			$cacheKey = 'groupsMatchFilter-'.$dn;
474
-			$groupMatchFilter = $this->connection->getFromCache($cacheKey);
475
-			if(!is_null($groupMatchFilter)) {
476
-				if($groupMatchFilter) {
477
-					$validGroupDNs[] = $dn;
478
-				}
479
-				continue;
480
-			}
481
-
482
-			// Check the base DN first. If this is not met already, we don't
483
-			// need to ask the server at all.
484
-			if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
485
-				$this->connection->writeToCache($cacheKey, false);
486
-				continue;
487
-			}
488
-
489
-			$result = $this->readAttribute($dn, 'cn', $this->connection->ldapGroupFilter);
490
-			if(is_array($result)) {
491
-				$this->connection->writeToCache($cacheKey, true);
492
-				$validGroupDNs[] = $dn;
493
-			} else {
494
-				$this->connection->writeToCache($cacheKey, false);
495
-			}
496
-
497
-		}
498
-		return $validGroupDNs;
499
-	}
500
-
501
-	/**
502
-	 * returns the internal Nextcloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
503
-	 * @param string $dn the dn of the user object
504
-	 * @param string $ldapName optional, the display name of the object
505
-	 * @return string|false with with the name to use in Nextcloud
506
-	 */
507
-	public function dn2username($fdn, $ldapName = null) {
508
-		//To avoid bypassing the base DN settings under certain circumstances
509
-		//with the group support, check whether the provided DN matches one of
510
-		//the given Bases
511
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
512
-			return false;
513
-		}
514
-
515
-		return $this->dn2ocname($fdn, $ldapName, true);
516
-	}
517
-
518
-	/**
519
-	 * returns an internal Nextcloud name for the given LDAP DN, false on DN outside of search DN
520
-	 *
521
-	 * @param string $fdn the dn of the user object
522
-	 * @param string $ldapName optional, the display name of the object
523
-	 * @param bool $isUser optional, whether it is a user object (otherwise group assumed)
524
-	 * @param bool|null $newlyMapped
525
-	 * @param array|null $record
526
-	 * @return false|string with with the name to use in Nextcloud
527
-	 */
528
-	public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped = null, array $record = null) {
529
-		$newlyMapped = false;
530
-		if($isUser) {
531
-			$mapper = $this->getUserMapper();
532
-			$nameAttribute = $this->connection->ldapUserDisplayName;
533
-		} else {
534
-			$mapper = $this->getGroupMapper();
535
-			$nameAttribute = $this->connection->ldapGroupDisplayName;
536
-		}
537
-
538
-		//let's try to retrieve the Nextcloud name from the mappings table
539
-		$ncName = $mapper->getNameByDN($fdn);
540
-		if(is_string($ncName)) {
541
-			return $ncName;
542
-		}
543
-
544
-		//second try: get the UUID and check if it is known. Then, update the DN and return the name.
545
-		$uuid = $this->getUUID($fdn, $isUser, $record);
546
-		if(is_string($uuid)) {
547
-			$ncName = $mapper->getNameByUUID($uuid);
548
-			if(is_string($ncName)) {
549
-				$mapper->setDNbyUUID($fdn, $uuid);
550
-				return $ncName;
551
-			}
552
-		} else {
553
-			//If the UUID can't be detected something is foul.
554
-			\OCP\Util::writeLog('user_ldap', 'Cannot determine UUID for '.$fdn.'. Skipping.', \OCP\Util::INFO);
555
-			return false;
556
-		}
557
-
558
-		if(is_null($ldapName)) {
559
-			$ldapName = $this->readAttribute($fdn, $nameAttribute);
560
-			if(!isset($ldapName[0]) && empty($ldapName[0])) {
561
-				\OCP\Util::writeLog('user_ldap', 'No or empty name for '.$fdn.'.', \OCP\Util::INFO);
562
-				return false;
563
-			}
564
-			$ldapName = $ldapName[0];
565
-		}
566
-
567
-		if($isUser) {
568
-			$usernameAttribute = strval($this->connection->ldapExpertUsernameAttr);
569
-			if ($usernameAttribute !== '') {
570
-				$username = $this->readAttribute($fdn, $usernameAttribute);
571
-				$username = $username[0];
572
-			} else {
573
-				$username = $uuid;
574
-			}
575
-			$intName = $this->sanitizeUsername($username);
576
-		} else {
577
-			$intName = $ldapName;
578
-		}
579
-
580
-		//a new user/group! Add it only if it doesn't conflict with other backend's users or existing groups
581
-		//disabling Cache is required to avoid that the new user is cached as not-existing in fooExists check
582
-		//NOTE: mind, disabling cache affects only this instance! Using it
583
-		// outside of core user management will still cache the user as non-existing.
584
-		$originalTTL = $this->connection->ldapCacheTTL;
585
-		$this->connection->setConfiguration(array('ldapCacheTTL' => 0));
586
-		if(($isUser && !\OCP\User::userExists($intName))
587
-			|| (!$isUser && !\OC::$server->getGroupManager()->groupExists($intName))) {
588
-			if($mapper->map($fdn, $intName, $uuid)) {
589
-				$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
590
-				$newlyMapped = true;
591
-				return $intName;
592
-			}
593
-		}
594
-		$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
595
-
596
-		$altName = $this->createAltInternalOwnCloudName($intName, $isUser);
597
-		if(is_string($altName) && $mapper->map($fdn, $altName, $uuid)) {
598
-			$newlyMapped = true;
599
-			return $altName;
600
-		}
601
-
602
-		//if everything else did not help..
603
-		\OCP\Util::writeLog('user_ldap', 'Could not create unique name for '.$fdn.'.', \OCP\Util::INFO);
604
-		return false;
605
-	}
606
-
607
-	/**
608
-	 * gives back the user names as they are used ownClod internally
609
-	 * @param array $ldapUsers as returned by fetchList()
610
-	 * @return array an array with the user names to use in Nextcloud
611
-	 *
612
-	 * gives back the user names as they are used ownClod internally
613
-	 */
614
-	public function nextcloudUserNames($ldapUsers) {
615
-		return $this->ldap2NextcloudNames($ldapUsers, true);
616
-	}
617
-
618
-	/**
619
-	 * gives back the group names as they are used ownClod internally
620
-	 * @param array $ldapGroups as returned by fetchList()
621
-	 * @return array an array with the group names to use in Nextcloud
622
-	 *
623
-	 * gives back the group names as they are used ownClod internally
624
-	 */
625
-	public function nextcloudGroupNames($ldapGroups) {
626
-		return $this->ldap2NextcloudNames($ldapGroups, false);
627
-	}
628
-
629
-	/**
630
-	 * @param array $ldapObjects as returned by fetchList()
631
-	 * @param bool $isUsers
632
-	 * @return array
633
-	 */
634
-	private function ldap2NextcloudNames($ldapObjects, $isUsers) {
635
-		if($isUsers) {
636
-			$nameAttribute = $this->connection->ldapUserDisplayName;
637
-			$sndAttribute  = $this->connection->ldapUserDisplayName2;
638
-		} else {
639
-			$nameAttribute = $this->connection->ldapGroupDisplayName;
640
-		}
641
-		$nextcloudNames = array();
642
-
643
-		foreach($ldapObjects as $ldapObject) {
644
-			$nameByLDAP = null;
645
-			if(    isset($ldapObject[$nameAttribute])
646
-				&& is_array($ldapObject[$nameAttribute])
647
-				&& isset($ldapObject[$nameAttribute][0])
648
-			) {
649
-				// might be set, but not necessarily. if so, we use it.
650
-				$nameByLDAP = $ldapObject[$nameAttribute][0];
651
-			}
652
-
653
-			$ncName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
654
-			if($ncName) {
655
-				$nextcloudNames[] = $ncName;
656
-				if($isUsers) {
657
-					//cache the user names so it does not need to be retrieved
658
-					//again later (e.g. sharing dialogue).
659
-					if(is_null($nameByLDAP)) {
660
-						continue;
661
-					}
662
-					$sndName = isset($ldapObject[$sndAttribute][0])
663
-						? $ldapObject[$sndAttribute][0] : '';
664
-					$this->cacheUserDisplayName($ncName, $nameByLDAP, $sndName);
665
-				}
666
-			}
667
-		}
668
-		return $nextcloudNames;
669
-	}
670
-
671
-	/**
672
-	 * caches the user display name
673
-	 * @param string $ocName the internal Nextcloud username
674
-	 * @param string|false $home the home directory path
675
-	 */
676
-	public function cacheUserHome($ocName, $home) {
677
-		$cacheKey = 'getHome'.$ocName;
678
-		$this->connection->writeToCache($cacheKey, $home);
679
-	}
680
-
681
-	/**
682
-	 * caches a user as existing
683
-	 * @param string $ocName the internal Nextcloud username
684
-	 */
685
-	public function cacheUserExists($ocName) {
686
-		$this->connection->writeToCache('userExists'.$ocName, true);
687
-	}
688
-
689
-	/**
690
-	 * caches the user display name
691
-	 * @param string $ocName the internal Nextcloud username
692
-	 * @param string $displayName the display name
693
-	 * @param string $displayName2 the second display name
694
-	 */
695
-	public function cacheUserDisplayName($ocName, $displayName, $displayName2 = '') {
696
-		$user = $this->userManager->get($ocName);
697
-		if($user === null) {
698
-			return;
699
-		}
700
-		$displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
701
-		$cacheKeyTrunk = 'getDisplayName';
702
-		$this->connection->writeToCache($cacheKeyTrunk.$ocName, $displayName);
703
-	}
704
-
705
-	/**
706
-	 * creates a unique name for internal Nextcloud use for users. Don't call it directly.
707
-	 * @param string $name the display name of the object
708
-	 * @return string|false with with the name to use in Nextcloud or false if unsuccessful
709
-	 *
710
-	 * Instead of using this method directly, call
711
-	 * createAltInternalOwnCloudName($name, true)
712
-	 */
713
-	private function _createAltInternalOwnCloudNameForUsers($name) {
714
-		$attempts = 0;
715
-		//while loop is just a precaution. If a name is not generated within
716
-		//20 attempts, something else is very wrong. Avoids infinite loop.
717
-		while($attempts < 20){
718
-			$altName = $name . '_' . rand(1000,9999);
719
-			if(!\OCP\User::userExists($altName)) {
720
-				return $altName;
721
-			}
722
-			$attempts++;
723
-		}
724
-		return false;
725
-	}
726
-
727
-	/**
728
-	 * creates a unique name for internal Nextcloud use for groups. Don't call it directly.
729
-	 * @param string $name the display name of the object
730
-	 * @return string|false with with the name to use in Nextcloud or false if unsuccessful.
731
-	 *
732
-	 * Instead of using this method directly, call
733
-	 * createAltInternalOwnCloudName($name, false)
734
-	 *
735
-	 * Group names are also used as display names, so we do a sequential
736
-	 * numbering, e.g. Developers_42 when there are 41 other groups called
737
-	 * "Developers"
738
-	 */
739
-	private function _createAltInternalOwnCloudNameForGroups($name) {
740
-		$usedNames = $this->groupMapper->getNamesBySearch($name, "", '_%');
741
-		if(!($usedNames) || count($usedNames) === 0) {
742
-			$lastNo = 1; //will become name_2
743
-		} else {
744
-			natsort($usedNames);
745
-			$lastName = array_pop($usedNames);
746
-			$lastNo = intval(substr($lastName, strrpos($lastName, '_') + 1));
747
-		}
748
-		$altName = $name.'_'.strval($lastNo+1);
749
-		unset($usedNames);
750
-
751
-		$attempts = 1;
752
-		while($attempts < 21){
753
-			// Check to be really sure it is unique
754
-			// while loop is just a precaution. If a name is not generated within
755
-			// 20 attempts, something else is very wrong. Avoids infinite loop.
756
-			if(!\OC::$server->getGroupManager()->groupExists($altName)) {
757
-				return $altName;
758
-			}
759
-			$altName = $name . '_' . ($lastNo + $attempts);
760
-			$attempts++;
761
-		}
762
-		return false;
763
-	}
764
-
765
-	/**
766
-	 * creates a unique name for internal Nextcloud use.
767
-	 * @param string $name the display name of the object
768
-	 * @param boolean $isUser whether name should be created for a user (true) or a group (false)
769
-	 * @return string|false with with the name to use in Nextcloud or false if unsuccessful
770
-	 */
771
-	private function createAltInternalOwnCloudName($name, $isUser) {
772
-		$originalTTL = $this->connection->ldapCacheTTL;
773
-		$this->connection->setConfiguration(array('ldapCacheTTL' => 0));
774
-		if($isUser) {
775
-			$altName = $this->_createAltInternalOwnCloudNameForUsers($name);
776
-		} else {
777
-			$altName = $this->_createAltInternalOwnCloudNameForGroups($name);
778
-		}
779
-		$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
780
-
781
-		return $altName;
782
-	}
783
-
784
-	/**
785
-	 * fetches a list of users according to a provided loginName and utilizing
786
-	 * the login filter.
787
-	 *
788
-	 * @param string $loginName
789
-	 * @param array $attributes optional, list of attributes to read
790
-	 * @return array
791
-	 */
792
-	public function fetchUsersByLoginName($loginName, $attributes = array('dn')) {
793
-		$loginName = $this->escapeFilterPart($loginName);
794
-		$filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
795
-		$users = $this->fetchListOfUsers($filter, $attributes);
796
-		return $users;
797
-	}
798
-
799
-	/**
800
-	 * counts the number of users according to a provided loginName and
801
-	 * utilizing the login filter.
802
-	 *
803
-	 * @param string $loginName
804
-	 * @return int
805
-	 */
806
-	public function countUsersByLoginName($loginName) {
807
-		$loginName = $this->escapeFilterPart($loginName);
808
-		$filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
809
-		$users = $this->countUsers($filter);
810
-		return $users;
811
-	}
812
-
813
-	/**
814
-	 * @param string $filter
815
-	 * @param string|string[] $attr
816
-	 * @param int $limit
817
-	 * @param int $offset
818
-	 * @param bool $forceApplyAttributes
819
-	 * @return array
820
-	 */
821
-	public function fetchListOfUsers($filter, $attr, $limit = null, $offset = null, $forceApplyAttributes = false) {
822
-		$ldapRecords = $this->searchUsers($filter, $attr, $limit, $offset);
823
-		$recordsToUpdate = $ldapRecords;
824
-		if(!$forceApplyAttributes) {
825
-			$isBackgroundJobModeAjax = $this->c->getConfig()
826
-					->getAppValue('core', 'backgroundjobs_mode', 'ajax') === 'ajax';
827
-			$recordsToUpdate = array_filter($ldapRecords, function($record) use ($isBackgroundJobModeAjax) {
828
-				$newlyMapped = false;
829
-				$uid = $this->dn2ocname($record['dn'][0], null, true, $newlyMapped, $record);
830
-				return ($uid !== false) && ($newlyMapped || $isBackgroundJobModeAjax);
831
-			});
832
-		}
833
-		$this->batchApplyUserAttributes($recordsToUpdate);
834
-		return $this->fetchList($ldapRecords, (count($attr) > 1));
835
-	}
836
-
837
-	/**
838
-	 * provided with an array of LDAP user records the method will fetch the
839
-	 * user object and requests it to process the freshly fetched attributes and
840
-	 * and their values
841
-	 * @param array $ldapRecords
842
-	 */
843
-	public function batchApplyUserAttributes(array $ldapRecords){
844
-		$displayNameAttribute = strtolower($this->connection->ldapUserDisplayName);
845
-		foreach($ldapRecords as $userRecord) {
846
-			if(!isset($userRecord[$displayNameAttribute])) {
847
-				// displayName is obligatory
848
-				continue;
849
-			}
850
-			$ocName  = $this->dn2ocname($userRecord['dn'][0], null, true);
851
-			if($ocName === false) {
852
-				continue;
853
-			}
854
-			$this->cacheUserExists($ocName);
855
-			$user = $this->userManager->get($ocName);
856
-			if($user instanceof OfflineUser) {
857
-				$user->unmark();
858
-				$user = $this->userManager->get($ocName);
859
-			}
860
-			if ($user !== null) {
861
-				$user->processAttributes($userRecord);
862
-			} else {
863
-				\OC::$server->getLogger()->debug(
864
-					"The ldap user manager returned null for $ocName",
865
-					['app'=>'user_ldap']
866
-				);
867
-			}
868
-		}
869
-	}
870
-
871
-	/**
872
-	 * @param string $filter
873
-	 * @param string|string[] $attr
874
-	 * @param int $limit
875
-	 * @param int $offset
876
-	 * @return array
877
-	 */
878
-	public function fetchListOfGroups($filter, $attr, $limit = null, $offset = null) {
879
-		return $this->fetchList($this->searchGroups($filter, $attr, $limit, $offset), (count($attr) > 1));
880
-	}
881
-
882
-	/**
883
-	 * @param array $list
884
-	 * @param bool $manyAttributes
885
-	 * @return array
886
-	 */
887
-	private function fetchList($list, $manyAttributes) {
888
-		if(is_array($list)) {
889
-			if($manyAttributes) {
890
-				return $list;
891
-			} else {
892
-				$list = array_reduce($list, function($carry, $item) {
893
-					$attribute = array_keys($item)[0];
894
-					$carry[] = $item[$attribute][0];
895
-					return $carry;
896
-				}, array());
897
-				return array_unique($list, SORT_LOCALE_STRING);
898
-			}
899
-		}
900
-
901
-		//error cause actually, maybe throw an exception in future.
902
-		return array();
903
-	}
904
-
905
-	/**
906
-	 * executes an LDAP search, optimized for Users
907
-	 * @param string $filter the LDAP filter for the search
908
-	 * @param string|string[] $attr optional, when a certain attribute shall be filtered out
909
-	 * @param integer $limit
910
-	 * @param integer $offset
911
-	 * @return array with the search result
912
-	 *
913
-	 * Executes an LDAP search
914
-	 */
915
-	public function searchUsers($filter, $attr = null, $limit = null, $offset = null) {
916
-		return $this->search($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
917
-	}
918
-
919
-	/**
920
-	 * @param string $filter
921
-	 * @param string|string[] $attr
922
-	 * @param int $limit
923
-	 * @param int $offset
924
-	 * @return false|int
925
-	 */
926
-	public function countUsers($filter, $attr = array('dn'), $limit = null, $offset = null) {
927
-		return $this->count($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
928
-	}
929
-
930
-	/**
931
-	 * executes an LDAP search, optimized for Groups
932
-	 * @param string $filter the LDAP filter for the search
933
-	 * @param string|string[] $attr optional, when a certain attribute shall be filtered out
934
-	 * @param integer $limit
935
-	 * @param integer $offset
936
-	 * @return array with the search result
937
-	 *
938
-	 * Executes an LDAP search
939
-	 */
940
-	public function searchGroups($filter, $attr = null, $limit = null, $offset = null) {
941
-		return $this->search($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
942
-	}
943
-
944
-	/**
945
-	 * returns the number of available groups
946
-	 * @param string $filter the LDAP search filter
947
-	 * @param string[] $attr optional
948
-	 * @param int|null $limit
949
-	 * @param int|null $offset
950
-	 * @return int|bool
951
-	 */
952
-	public function countGroups($filter, $attr = array('dn'), $limit = null, $offset = null) {
953
-		return $this->count($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
954
-	}
955
-
956
-	/**
957
-	 * returns the number of available objects on the base DN
958
-	 *
959
-	 * @param int|null $limit
960
-	 * @param int|null $offset
961
-	 * @return int|bool
962
-	 */
963
-	public function countObjects($limit = null, $offset = null) {
964
-		return $this->count('objectclass=*', $this->connection->ldapBase, array('dn'), $limit, $offset);
965
-	}
966
-
967
-	/**
968
-	 * Returns the LDAP handler
969
-	 * @throws \OC\ServerNotAvailableException
970
-	 */
971
-
972
-	/**
973
-	 * @return mixed
974
-	 * @throws \OC\ServerNotAvailableException
975
-	 */
976
-	private function invokeLDAPMethod() {
977
-		$arguments = func_get_args();
978
-		$command = array_shift($arguments);
979
-		$cr = array_shift($arguments);
980
-		if (!method_exists($this->ldap, $command)) {
981
-			return null;
982
-		}
983
-		array_unshift($arguments, $cr);
984
-		// php no longer supports call-time pass-by-reference
985
-		// thus cannot support controlPagedResultResponse as the third argument
986
-		// is a reference
987
-		$doMethod = function () use ($command, &$arguments) {
988
-			if ($command == 'controlPagedResultResponse') {
989
-				throw new \InvalidArgumentException('Invoker does not support controlPagedResultResponse, call LDAP Wrapper directly instead.');
990
-			} else {
991
-				return call_user_func_array(array($this->ldap, $command), $arguments);
992
-			}
993
-		};
994
-		try {
995
-			$ret = $doMethod();
996
-		} catch (ServerNotAvailableException $e) {
997
-			/* Server connection lost, attempt to reestablish it
338
+    /**
339
+     * Set password for an LDAP user identified by a DN
340
+     *
341
+     * @param string $userDN the user in question
342
+     * @param string $password the new password
343
+     * @return bool
344
+     * @throws HintException
345
+     * @throws \Exception
346
+     */
347
+    public function setPassword($userDN, $password) {
348
+        if(intval($this->connection->turnOnPasswordChange) !== 1) {
349
+            throw new \Exception('LDAP password changes are disabled.');
350
+        }
351
+        $cr = $this->connection->getConnectionResource();
352
+        if(!$this->ldap->isResource($cr)) {
353
+            //LDAP not available
354
+            \OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
355
+            return false;
356
+        }
357
+        try {
358
+            return @$this->invokeLDAPMethod('modReplace', $cr, $userDN, $password);
359
+        } catch(ConstraintViolationException $e) {
360
+            throw new HintException('Password change rejected.', \OC::$server->getL10N('user_ldap')->t('Password change rejected. Hint: ').$e->getMessage(), $e->getCode());
361
+        }
362
+    }
363
+
364
+    /**
365
+     * checks whether the given attributes value is probably a DN
366
+     * @param string $attr the attribute in question
367
+     * @return boolean if so true, otherwise false
368
+     */
369
+    private function resemblesDN($attr) {
370
+        $resemblingAttributes = array(
371
+            'dn',
372
+            'uniquemember',
373
+            'member',
374
+            // memberOf is an "operational" attribute, without a definition in any RFC
375
+            'memberof'
376
+        );
377
+        return in_array($attr, $resemblingAttributes);
378
+    }
379
+
380
+    /**
381
+     * checks whether the given string is probably a DN
382
+     * @param string $string
383
+     * @return boolean
384
+     */
385
+    public function stringResemblesDN($string) {
386
+        $r = $this->ldap->explodeDN($string, 0);
387
+        // if exploding a DN succeeds and does not end up in
388
+        // an empty array except for $r[count] being 0.
389
+        return (is_array($r) && count($r) > 1);
390
+    }
391
+
392
+    /**
393
+     * returns a DN-string that is cleaned from not domain parts, e.g.
394
+     * cn=foo,cn=bar,dc=foobar,dc=server,dc=org
395
+     * becomes dc=foobar,dc=server,dc=org
396
+     * @param string $dn
397
+     * @return string
398
+     */
399
+    public function getDomainDNFromDN($dn) {
400
+        $allParts = $this->ldap->explodeDN($dn, 0);
401
+        if($allParts === false) {
402
+            //not a valid DN
403
+            return '';
404
+        }
405
+        $domainParts = array();
406
+        $dcFound = false;
407
+        foreach($allParts as $part) {
408
+            if(!$dcFound && strpos($part, 'dc=') === 0) {
409
+                $dcFound = true;
410
+            }
411
+            if($dcFound) {
412
+                $domainParts[] = $part;
413
+            }
414
+        }
415
+        $domainDN = implode(',', $domainParts);
416
+        return $domainDN;
417
+    }
418
+
419
+    /**
420
+     * returns the LDAP DN for the given internal Nextcloud name of the group
421
+     * @param string $name the Nextcloud name in question
422
+     * @return string|false LDAP DN on success, otherwise false
423
+     */
424
+    public function groupname2dn($name) {
425
+        return $this->groupMapper->getDNByName($name);
426
+    }
427
+
428
+    /**
429
+     * returns the LDAP DN for the given internal Nextcloud name of the user
430
+     * @param string $name the Nextcloud name in question
431
+     * @return string|false with the LDAP DN on success, otherwise false
432
+     */
433
+    public function username2dn($name) {
434
+        $fdn = $this->userMapper->getDNByName($name);
435
+
436
+        //Check whether the DN belongs to the Base, to avoid issues on multi-
437
+        //server setups
438
+        if(is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
439
+            return $fdn;
440
+        }
441
+
442
+        return false;
443
+    }
444
+
445
+    /**
446
+     * returns the internal Nextcloud name for the given LDAP DN of the group, false on DN outside of search DN or failure
447
+     * @param string $fdn the dn of the group object
448
+     * @param string $ldapName optional, the display name of the object
449
+     * @return string|false with the name to use in Nextcloud, false on DN outside of search DN
450
+     */
451
+    public function dn2groupname($fdn, $ldapName = null) {
452
+        //To avoid bypassing the base DN settings under certain circumstances
453
+        //with the group support, check whether the provided DN matches one of
454
+        //the given Bases
455
+        if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
456
+            return false;
457
+        }
458
+
459
+        return $this->dn2ocname($fdn, $ldapName, false);
460
+    }
461
+
462
+    /**
463
+     * accepts an array of group DNs and tests whether they match the user
464
+     * filter by doing read operations against the group entries. Returns an
465
+     * array of DNs that match the filter.
466
+     *
467
+     * @param string[] $groupDNs
468
+     * @return string[]
469
+     */
470
+    public function groupsMatchFilter($groupDNs) {
471
+        $validGroupDNs = [];
472
+        foreach($groupDNs as $dn) {
473
+            $cacheKey = 'groupsMatchFilter-'.$dn;
474
+            $groupMatchFilter = $this->connection->getFromCache($cacheKey);
475
+            if(!is_null($groupMatchFilter)) {
476
+                if($groupMatchFilter) {
477
+                    $validGroupDNs[] = $dn;
478
+                }
479
+                continue;
480
+            }
481
+
482
+            // Check the base DN first. If this is not met already, we don't
483
+            // need to ask the server at all.
484
+            if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
485
+                $this->connection->writeToCache($cacheKey, false);
486
+                continue;
487
+            }
488
+
489
+            $result = $this->readAttribute($dn, 'cn', $this->connection->ldapGroupFilter);
490
+            if(is_array($result)) {
491
+                $this->connection->writeToCache($cacheKey, true);
492
+                $validGroupDNs[] = $dn;
493
+            } else {
494
+                $this->connection->writeToCache($cacheKey, false);
495
+            }
496
+
497
+        }
498
+        return $validGroupDNs;
499
+    }
500
+
501
+    /**
502
+     * returns the internal Nextcloud name for the given LDAP DN of the user, false on DN outside of search DN or failure
503
+     * @param string $dn the dn of the user object
504
+     * @param string $ldapName optional, the display name of the object
505
+     * @return string|false with with the name to use in Nextcloud
506
+     */
507
+    public function dn2username($fdn, $ldapName = null) {
508
+        //To avoid bypassing the base DN settings under certain circumstances
509
+        //with the group support, check whether the provided DN matches one of
510
+        //the given Bases
511
+        if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
512
+            return false;
513
+        }
514
+
515
+        return $this->dn2ocname($fdn, $ldapName, true);
516
+    }
517
+
518
+    /**
519
+     * returns an internal Nextcloud name for the given LDAP DN, false on DN outside of search DN
520
+     *
521
+     * @param string $fdn the dn of the user object
522
+     * @param string $ldapName optional, the display name of the object
523
+     * @param bool $isUser optional, whether it is a user object (otherwise group assumed)
524
+     * @param bool|null $newlyMapped
525
+     * @param array|null $record
526
+     * @return false|string with with the name to use in Nextcloud
527
+     */
528
+    public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped = null, array $record = null) {
529
+        $newlyMapped = false;
530
+        if($isUser) {
531
+            $mapper = $this->getUserMapper();
532
+            $nameAttribute = $this->connection->ldapUserDisplayName;
533
+        } else {
534
+            $mapper = $this->getGroupMapper();
535
+            $nameAttribute = $this->connection->ldapGroupDisplayName;
536
+        }
537
+
538
+        //let's try to retrieve the Nextcloud name from the mappings table
539
+        $ncName = $mapper->getNameByDN($fdn);
540
+        if(is_string($ncName)) {
541
+            return $ncName;
542
+        }
543
+
544
+        //second try: get the UUID and check if it is known. Then, update the DN and return the name.
545
+        $uuid = $this->getUUID($fdn, $isUser, $record);
546
+        if(is_string($uuid)) {
547
+            $ncName = $mapper->getNameByUUID($uuid);
548
+            if(is_string($ncName)) {
549
+                $mapper->setDNbyUUID($fdn, $uuid);
550
+                return $ncName;
551
+            }
552
+        } else {
553
+            //If the UUID can't be detected something is foul.
554
+            \OCP\Util::writeLog('user_ldap', 'Cannot determine UUID for '.$fdn.'. Skipping.', \OCP\Util::INFO);
555
+            return false;
556
+        }
557
+
558
+        if(is_null($ldapName)) {
559
+            $ldapName = $this->readAttribute($fdn, $nameAttribute);
560
+            if(!isset($ldapName[0]) && empty($ldapName[0])) {
561
+                \OCP\Util::writeLog('user_ldap', 'No or empty name for '.$fdn.'.', \OCP\Util::INFO);
562
+                return false;
563
+            }
564
+            $ldapName = $ldapName[0];
565
+        }
566
+
567
+        if($isUser) {
568
+            $usernameAttribute = strval($this->connection->ldapExpertUsernameAttr);
569
+            if ($usernameAttribute !== '') {
570
+                $username = $this->readAttribute($fdn, $usernameAttribute);
571
+                $username = $username[0];
572
+            } else {
573
+                $username = $uuid;
574
+            }
575
+            $intName = $this->sanitizeUsername($username);
576
+        } else {
577
+            $intName = $ldapName;
578
+        }
579
+
580
+        //a new user/group! Add it only if it doesn't conflict with other backend's users or existing groups
581
+        //disabling Cache is required to avoid that the new user is cached as not-existing in fooExists check
582
+        //NOTE: mind, disabling cache affects only this instance! Using it
583
+        // outside of core user management will still cache the user as non-existing.
584
+        $originalTTL = $this->connection->ldapCacheTTL;
585
+        $this->connection->setConfiguration(array('ldapCacheTTL' => 0));
586
+        if(($isUser && !\OCP\User::userExists($intName))
587
+            || (!$isUser && !\OC::$server->getGroupManager()->groupExists($intName))) {
588
+            if($mapper->map($fdn, $intName, $uuid)) {
589
+                $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
590
+                $newlyMapped = true;
591
+                return $intName;
592
+            }
593
+        }
594
+        $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
595
+
596
+        $altName = $this->createAltInternalOwnCloudName($intName, $isUser);
597
+        if(is_string($altName) && $mapper->map($fdn, $altName, $uuid)) {
598
+            $newlyMapped = true;
599
+            return $altName;
600
+        }
601
+
602
+        //if everything else did not help..
603
+        \OCP\Util::writeLog('user_ldap', 'Could not create unique name for '.$fdn.'.', \OCP\Util::INFO);
604
+        return false;
605
+    }
606
+
607
+    /**
608
+     * gives back the user names as they are used ownClod internally
609
+     * @param array $ldapUsers as returned by fetchList()
610
+     * @return array an array with the user names to use in Nextcloud
611
+     *
612
+     * gives back the user names as they are used ownClod internally
613
+     */
614
+    public function nextcloudUserNames($ldapUsers) {
615
+        return $this->ldap2NextcloudNames($ldapUsers, true);
616
+    }
617
+
618
+    /**
619
+     * gives back the group names as they are used ownClod internally
620
+     * @param array $ldapGroups as returned by fetchList()
621
+     * @return array an array with the group names to use in Nextcloud
622
+     *
623
+     * gives back the group names as they are used ownClod internally
624
+     */
625
+    public function nextcloudGroupNames($ldapGroups) {
626
+        return $this->ldap2NextcloudNames($ldapGroups, false);
627
+    }
628
+
629
+    /**
630
+     * @param array $ldapObjects as returned by fetchList()
631
+     * @param bool $isUsers
632
+     * @return array
633
+     */
634
+    private function ldap2NextcloudNames($ldapObjects, $isUsers) {
635
+        if($isUsers) {
636
+            $nameAttribute = $this->connection->ldapUserDisplayName;
637
+            $sndAttribute  = $this->connection->ldapUserDisplayName2;
638
+        } else {
639
+            $nameAttribute = $this->connection->ldapGroupDisplayName;
640
+        }
641
+        $nextcloudNames = array();
642
+
643
+        foreach($ldapObjects as $ldapObject) {
644
+            $nameByLDAP = null;
645
+            if(    isset($ldapObject[$nameAttribute])
646
+                && is_array($ldapObject[$nameAttribute])
647
+                && isset($ldapObject[$nameAttribute][0])
648
+            ) {
649
+                // might be set, but not necessarily. if so, we use it.
650
+                $nameByLDAP = $ldapObject[$nameAttribute][0];
651
+            }
652
+
653
+            $ncName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
654
+            if($ncName) {
655
+                $nextcloudNames[] = $ncName;
656
+                if($isUsers) {
657
+                    //cache the user names so it does not need to be retrieved
658
+                    //again later (e.g. sharing dialogue).
659
+                    if(is_null($nameByLDAP)) {
660
+                        continue;
661
+                    }
662
+                    $sndName = isset($ldapObject[$sndAttribute][0])
663
+                        ? $ldapObject[$sndAttribute][0] : '';
664
+                    $this->cacheUserDisplayName($ncName, $nameByLDAP, $sndName);
665
+                }
666
+            }
667
+        }
668
+        return $nextcloudNames;
669
+    }
670
+
671
+    /**
672
+     * caches the user display name
673
+     * @param string $ocName the internal Nextcloud username
674
+     * @param string|false $home the home directory path
675
+     */
676
+    public function cacheUserHome($ocName, $home) {
677
+        $cacheKey = 'getHome'.$ocName;
678
+        $this->connection->writeToCache($cacheKey, $home);
679
+    }
680
+
681
+    /**
682
+     * caches a user as existing
683
+     * @param string $ocName the internal Nextcloud username
684
+     */
685
+    public function cacheUserExists($ocName) {
686
+        $this->connection->writeToCache('userExists'.$ocName, true);
687
+    }
688
+
689
+    /**
690
+     * caches the user display name
691
+     * @param string $ocName the internal Nextcloud username
692
+     * @param string $displayName the display name
693
+     * @param string $displayName2 the second display name
694
+     */
695
+    public function cacheUserDisplayName($ocName, $displayName, $displayName2 = '') {
696
+        $user = $this->userManager->get($ocName);
697
+        if($user === null) {
698
+            return;
699
+        }
700
+        $displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
701
+        $cacheKeyTrunk = 'getDisplayName';
702
+        $this->connection->writeToCache($cacheKeyTrunk.$ocName, $displayName);
703
+    }
704
+
705
+    /**
706
+     * creates a unique name for internal Nextcloud use for users. Don't call it directly.
707
+     * @param string $name the display name of the object
708
+     * @return string|false with with the name to use in Nextcloud or false if unsuccessful
709
+     *
710
+     * Instead of using this method directly, call
711
+     * createAltInternalOwnCloudName($name, true)
712
+     */
713
+    private function _createAltInternalOwnCloudNameForUsers($name) {
714
+        $attempts = 0;
715
+        //while loop is just a precaution. If a name is not generated within
716
+        //20 attempts, something else is very wrong. Avoids infinite loop.
717
+        while($attempts < 20){
718
+            $altName = $name . '_' . rand(1000,9999);
719
+            if(!\OCP\User::userExists($altName)) {
720
+                return $altName;
721
+            }
722
+            $attempts++;
723
+        }
724
+        return false;
725
+    }
726
+
727
+    /**
728
+     * creates a unique name for internal Nextcloud use for groups. Don't call it directly.
729
+     * @param string $name the display name of the object
730
+     * @return string|false with with the name to use in Nextcloud or false if unsuccessful.
731
+     *
732
+     * Instead of using this method directly, call
733
+     * createAltInternalOwnCloudName($name, false)
734
+     *
735
+     * Group names are also used as display names, so we do a sequential
736
+     * numbering, e.g. Developers_42 when there are 41 other groups called
737
+     * "Developers"
738
+     */
739
+    private function _createAltInternalOwnCloudNameForGroups($name) {
740
+        $usedNames = $this->groupMapper->getNamesBySearch($name, "", '_%');
741
+        if(!($usedNames) || count($usedNames) === 0) {
742
+            $lastNo = 1; //will become name_2
743
+        } else {
744
+            natsort($usedNames);
745
+            $lastName = array_pop($usedNames);
746
+            $lastNo = intval(substr($lastName, strrpos($lastName, '_') + 1));
747
+        }
748
+        $altName = $name.'_'.strval($lastNo+1);
749
+        unset($usedNames);
750
+
751
+        $attempts = 1;
752
+        while($attempts < 21){
753
+            // Check to be really sure it is unique
754
+            // while loop is just a precaution. If a name is not generated within
755
+            // 20 attempts, something else is very wrong. Avoids infinite loop.
756
+            if(!\OC::$server->getGroupManager()->groupExists($altName)) {
757
+                return $altName;
758
+            }
759
+            $altName = $name . '_' . ($lastNo + $attempts);
760
+            $attempts++;
761
+        }
762
+        return false;
763
+    }
764
+
765
+    /**
766
+     * creates a unique name for internal Nextcloud use.
767
+     * @param string $name the display name of the object
768
+     * @param boolean $isUser whether name should be created for a user (true) or a group (false)
769
+     * @return string|false with with the name to use in Nextcloud or false if unsuccessful
770
+     */
771
+    private function createAltInternalOwnCloudName($name, $isUser) {
772
+        $originalTTL = $this->connection->ldapCacheTTL;
773
+        $this->connection->setConfiguration(array('ldapCacheTTL' => 0));
774
+        if($isUser) {
775
+            $altName = $this->_createAltInternalOwnCloudNameForUsers($name);
776
+        } else {
777
+            $altName = $this->_createAltInternalOwnCloudNameForGroups($name);
778
+        }
779
+        $this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
780
+
781
+        return $altName;
782
+    }
783
+
784
+    /**
785
+     * fetches a list of users according to a provided loginName and utilizing
786
+     * the login filter.
787
+     *
788
+     * @param string $loginName
789
+     * @param array $attributes optional, list of attributes to read
790
+     * @return array
791
+     */
792
+    public function fetchUsersByLoginName($loginName, $attributes = array('dn')) {
793
+        $loginName = $this->escapeFilterPart($loginName);
794
+        $filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
795
+        $users = $this->fetchListOfUsers($filter, $attributes);
796
+        return $users;
797
+    }
798
+
799
+    /**
800
+     * counts the number of users according to a provided loginName and
801
+     * utilizing the login filter.
802
+     *
803
+     * @param string $loginName
804
+     * @return int
805
+     */
806
+    public function countUsersByLoginName($loginName) {
807
+        $loginName = $this->escapeFilterPart($loginName);
808
+        $filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
809
+        $users = $this->countUsers($filter);
810
+        return $users;
811
+    }
812
+
813
+    /**
814
+     * @param string $filter
815
+     * @param string|string[] $attr
816
+     * @param int $limit
817
+     * @param int $offset
818
+     * @param bool $forceApplyAttributes
819
+     * @return array
820
+     */
821
+    public function fetchListOfUsers($filter, $attr, $limit = null, $offset = null, $forceApplyAttributes = false) {
822
+        $ldapRecords = $this->searchUsers($filter, $attr, $limit, $offset);
823
+        $recordsToUpdate = $ldapRecords;
824
+        if(!$forceApplyAttributes) {
825
+            $isBackgroundJobModeAjax = $this->c->getConfig()
826
+                    ->getAppValue('core', 'backgroundjobs_mode', 'ajax') === 'ajax';
827
+            $recordsToUpdate = array_filter($ldapRecords, function($record) use ($isBackgroundJobModeAjax) {
828
+                $newlyMapped = false;
829
+                $uid = $this->dn2ocname($record['dn'][0], null, true, $newlyMapped, $record);
830
+                return ($uid !== false) && ($newlyMapped || $isBackgroundJobModeAjax);
831
+            });
832
+        }
833
+        $this->batchApplyUserAttributes($recordsToUpdate);
834
+        return $this->fetchList($ldapRecords, (count($attr) > 1));
835
+    }
836
+
837
+    /**
838
+     * provided with an array of LDAP user records the method will fetch the
839
+     * user object and requests it to process the freshly fetched attributes and
840
+     * and their values
841
+     * @param array $ldapRecords
842
+     */
843
+    public function batchApplyUserAttributes(array $ldapRecords){
844
+        $displayNameAttribute = strtolower($this->connection->ldapUserDisplayName);
845
+        foreach($ldapRecords as $userRecord) {
846
+            if(!isset($userRecord[$displayNameAttribute])) {
847
+                // displayName is obligatory
848
+                continue;
849
+            }
850
+            $ocName  = $this->dn2ocname($userRecord['dn'][0], null, true);
851
+            if($ocName === false) {
852
+                continue;
853
+            }
854
+            $this->cacheUserExists($ocName);
855
+            $user = $this->userManager->get($ocName);
856
+            if($user instanceof OfflineUser) {
857
+                $user->unmark();
858
+                $user = $this->userManager->get($ocName);
859
+            }
860
+            if ($user !== null) {
861
+                $user->processAttributes($userRecord);
862
+            } else {
863
+                \OC::$server->getLogger()->debug(
864
+                    "The ldap user manager returned null for $ocName",
865
+                    ['app'=>'user_ldap']
866
+                );
867
+            }
868
+        }
869
+    }
870
+
871
+    /**
872
+     * @param string $filter
873
+     * @param string|string[] $attr
874
+     * @param int $limit
875
+     * @param int $offset
876
+     * @return array
877
+     */
878
+    public function fetchListOfGroups($filter, $attr, $limit = null, $offset = null) {
879
+        return $this->fetchList($this->searchGroups($filter, $attr, $limit, $offset), (count($attr) > 1));
880
+    }
881
+
882
+    /**
883
+     * @param array $list
884
+     * @param bool $manyAttributes
885
+     * @return array
886
+     */
887
+    private function fetchList($list, $manyAttributes) {
888
+        if(is_array($list)) {
889
+            if($manyAttributes) {
890
+                return $list;
891
+            } else {
892
+                $list = array_reduce($list, function($carry, $item) {
893
+                    $attribute = array_keys($item)[0];
894
+                    $carry[] = $item[$attribute][0];
895
+                    return $carry;
896
+                }, array());
897
+                return array_unique($list, SORT_LOCALE_STRING);
898
+            }
899
+        }
900
+
901
+        //error cause actually, maybe throw an exception in future.
902
+        return array();
903
+    }
904
+
905
+    /**
906
+     * executes an LDAP search, optimized for Users
907
+     * @param string $filter the LDAP filter for the search
908
+     * @param string|string[] $attr optional, when a certain attribute shall be filtered out
909
+     * @param integer $limit
910
+     * @param integer $offset
911
+     * @return array with the search result
912
+     *
913
+     * Executes an LDAP search
914
+     */
915
+    public function searchUsers($filter, $attr = null, $limit = null, $offset = null) {
916
+        return $this->search($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
917
+    }
918
+
919
+    /**
920
+     * @param string $filter
921
+     * @param string|string[] $attr
922
+     * @param int $limit
923
+     * @param int $offset
924
+     * @return false|int
925
+     */
926
+    public function countUsers($filter, $attr = array('dn'), $limit = null, $offset = null) {
927
+        return $this->count($filter, $this->connection->ldapBaseUsers, $attr, $limit, $offset);
928
+    }
929
+
930
+    /**
931
+     * executes an LDAP search, optimized for Groups
932
+     * @param string $filter the LDAP filter for the search
933
+     * @param string|string[] $attr optional, when a certain attribute shall be filtered out
934
+     * @param integer $limit
935
+     * @param integer $offset
936
+     * @return array with the search result
937
+     *
938
+     * Executes an LDAP search
939
+     */
940
+    public function searchGroups($filter, $attr = null, $limit = null, $offset = null) {
941
+        return $this->search($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
942
+    }
943
+
944
+    /**
945
+     * returns the number of available groups
946
+     * @param string $filter the LDAP search filter
947
+     * @param string[] $attr optional
948
+     * @param int|null $limit
949
+     * @param int|null $offset
950
+     * @return int|bool
951
+     */
952
+    public function countGroups($filter, $attr = array('dn'), $limit = null, $offset = null) {
953
+        return $this->count($filter, $this->connection->ldapBaseGroups, $attr, $limit, $offset);
954
+    }
955
+
956
+    /**
957
+     * returns the number of available objects on the base DN
958
+     *
959
+     * @param int|null $limit
960
+     * @param int|null $offset
961
+     * @return int|bool
962
+     */
963
+    public function countObjects($limit = null, $offset = null) {
964
+        return $this->count('objectclass=*', $this->connection->ldapBase, array('dn'), $limit, $offset);
965
+    }
966
+
967
+    /**
968
+     * Returns the LDAP handler
969
+     * @throws \OC\ServerNotAvailableException
970
+     */
971
+
972
+    /**
973
+     * @return mixed
974
+     * @throws \OC\ServerNotAvailableException
975
+     */
976
+    private function invokeLDAPMethod() {
977
+        $arguments = func_get_args();
978
+        $command = array_shift($arguments);
979
+        $cr = array_shift($arguments);
980
+        if (!method_exists($this->ldap, $command)) {
981
+            return null;
982
+        }
983
+        array_unshift($arguments, $cr);
984
+        // php no longer supports call-time pass-by-reference
985
+        // thus cannot support controlPagedResultResponse as the third argument
986
+        // is a reference
987
+        $doMethod = function () use ($command, &$arguments) {
988
+            if ($command == 'controlPagedResultResponse') {
989
+                throw new \InvalidArgumentException('Invoker does not support controlPagedResultResponse, call LDAP Wrapper directly instead.');
990
+            } else {
991
+                return call_user_func_array(array($this->ldap, $command), $arguments);
992
+            }
993
+        };
994
+        try {
995
+            $ret = $doMethod();
996
+        } catch (ServerNotAvailableException $e) {
997
+            /* Server connection lost, attempt to reestablish it
998 998
 			 * Maybe implement exponential backoff?
999 999
 			 * This was enough to get solr indexer working which has large delays between LDAP fetches.
1000 1000
 			 */
1001
-			\OCP\Util::writeLog('user_ldap', "Connection lost on $command, attempting to reestablish.", \OCP\Util::DEBUG);
1002
-			$this->connection->resetConnectionResource();
1003
-			$cr = $this->connection->getConnectionResource();
1004
-
1005
-			if(!$this->ldap->isResource($cr)) {
1006
-				// Seems like we didn't find any resource.
1007
-				\OCP\Util::writeLog('user_ldap', "Could not $command, because resource is missing.", \OCP\Util::DEBUG);
1008
-				throw $e;
1009
-			}
1010
-
1011
-			$arguments[0] = array_pad([], count($arguments[0]), $cr);
1012
-			$ret = $doMethod();
1013
-		}
1014
-		return $ret;
1015
-	}
1016
-
1017
-	/**
1018
-	 * retrieved. Results will according to the order in the array.
1019
-	 * @param int $limit optional, maximum results to be counted
1020
-	 * @param int $offset optional, a starting point
1021
-	 * @return array|false array with the search result as first value and pagedSearchOK as
1022
-	 * second | false if not successful
1023
-	 * @throws \OC\ServerNotAvailableException
1024
-	 */
1025
-	private function executeSearch($filter, $base, &$attr = null, $limit = null, $offset = null) {
1026
-		if(!is_null($attr) && !is_array($attr)) {
1027
-			$attr = array(mb_strtolower($attr, 'UTF-8'));
1028
-		}
1029
-
1030
-		// See if we have a resource, in case not cancel with message
1031
-		$cr = $this->connection->getConnectionResource();
1032
-		if(!$this->ldap->isResource($cr)) {
1033
-			// Seems like we didn't find any resource.
1034
-			// Return an empty array just like before.
1035
-			\OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG);
1036
-			return false;
1037
-		}
1038
-
1039
-		//check whether paged search should be attempted
1040
-		$pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, intval($limit), $offset);
1041
-
1042
-		$linkResources = array_pad(array(), count($base), $cr);
1043
-		$sr = $this->invokeLDAPMethod('search', $linkResources, $base, $filter, $attr);
1044
-		// cannot use $cr anymore, might have changed in the previous call!
1045
-		$error = $this->ldap->errno($this->connection->getConnectionResource());
1046
-		if(!is_array($sr) || $error !== 0) {
1047
-			\OCP\Util::writeLog('user_ldap', 'Attempt for Paging?  '.print_r($pagedSearchOK, true), \OCP\Util::ERROR);
1048
-			return false;
1049
-		}
1050
-
1051
-		return array($sr, $pagedSearchOK);
1052
-	}
1053
-
1054
-	/**
1055
-	 * processes an LDAP paged search operation
1056
-	 * @param array $sr the array containing the LDAP search resources
1057
-	 * @param string $filter the LDAP filter for the search
1058
-	 * @param array $base an array containing the LDAP subtree(s) that shall be searched
1059
-	 * @param int $iFoundItems number of results in the single search operation
1060
-	 * @param int $limit maximum results to be counted
1061
-	 * @param int $offset a starting point
1062
-	 * @param bool $pagedSearchOK whether a paged search has been executed
1063
-	 * @param bool $skipHandling required for paged search when cookies to
1064
-	 * prior results need to be gained
1065
-	 * @return bool cookie validity, true if we have more pages, false otherwise.
1066
-	 */
1067
-	private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) {
1068
-		$cookie = null;
1069
-		if($pagedSearchOK) {
1070
-			$cr = $this->connection->getConnectionResource();
1071
-			foreach($sr as $key => $res) {
1072
-				if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
1073
-					$this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
1074
-				}
1075
-			}
1076
-
1077
-			//browsing through prior pages to get the cookie for the new one
1078
-			if($skipHandling) {
1079
-				return false;
1080
-			}
1081
-			// if count is bigger, then the server does not support
1082
-			// paged search. Instead, he did a normal search. We set a
1083
-			// flag here, so the callee knows how to deal with it.
1084
-			if($iFoundItems <= $limit) {
1085
-				$this->pagedSearchedSuccessful = true;
1086
-			}
1087
-		} else {
1088
-			if(!is_null($limit)) {
1089
-				\OCP\Util::writeLog('user_ldap', 'Paged search was not available', \OCP\Util::INFO);
1090
-			}
1091
-		}
1092
-		/* ++ Fixing RHDS searches with pages with zero results ++
1001
+            \OCP\Util::writeLog('user_ldap', "Connection lost on $command, attempting to reestablish.", \OCP\Util::DEBUG);
1002
+            $this->connection->resetConnectionResource();
1003
+            $cr = $this->connection->getConnectionResource();
1004
+
1005
+            if(!$this->ldap->isResource($cr)) {
1006
+                // Seems like we didn't find any resource.
1007
+                \OCP\Util::writeLog('user_ldap', "Could not $command, because resource is missing.", \OCP\Util::DEBUG);
1008
+                throw $e;
1009
+            }
1010
+
1011
+            $arguments[0] = array_pad([], count($arguments[0]), $cr);
1012
+            $ret = $doMethod();
1013
+        }
1014
+        return $ret;
1015
+    }
1016
+
1017
+    /**
1018
+     * retrieved. Results will according to the order in the array.
1019
+     * @param int $limit optional, maximum results to be counted
1020
+     * @param int $offset optional, a starting point
1021
+     * @return array|false array with the search result as first value and pagedSearchOK as
1022
+     * second | false if not successful
1023
+     * @throws \OC\ServerNotAvailableException
1024
+     */
1025
+    private function executeSearch($filter, $base, &$attr = null, $limit = null, $offset = null) {
1026
+        if(!is_null($attr) && !is_array($attr)) {
1027
+            $attr = array(mb_strtolower($attr, 'UTF-8'));
1028
+        }
1029
+
1030
+        // See if we have a resource, in case not cancel with message
1031
+        $cr = $this->connection->getConnectionResource();
1032
+        if(!$this->ldap->isResource($cr)) {
1033
+            // Seems like we didn't find any resource.
1034
+            // Return an empty array just like before.
1035
+            \OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG);
1036
+            return false;
1037
+        }
1038
+
1039
+        //check whether paged search should be attempted
1040
+        $pagedSearchOK = $this->initPagedSearch($filter, $base, $attr, intval($limit), $offset);
1041
+
1042
+        $linkResources = array_pad(array(), count($base), $cr);
1043
+        $sr = $this->invokeLDAPMethod('search', $linkResources, $base, $filter, $attr);
1044
+        // cannot use $cr anymore, might have changed in the previous call!
1045
+        $error = $this->ldap->errno($this->connection->getConnectionResource());
1046
+        if(!is_array($sr) || $error !== 0) {
1047
+            \OCP\Util::writeLog('user_ldap', 'Attempt for Paging?  '.print_r($pagedSearchOK, true), \OCP\Util::ERROR);
1048
+            return false;
1049
+        }
1050
+
1051
+        return array($sr, $pagedSearchOK);
1052
+    }
1053
+
1054
+    /**
1055
+     * processes an LDAP paged search operation
1056
+     * @param array $sr the array containing the LDAP search resources
1057
+     * @param string $filter the LDAP filter for the search
1058
+     * @param array $base an array containing the LDAP subtree(s) that shall be searched
1059
+     * @param int $iFoundItems number of results in the single search operation
1060
+     * @param int $limit maximum results to be counted
1061
+     * @param int $offset a starting point
1062
+     * @param bool $pagedSearchOK whether a paged search has been executed
1063
+     * @param bool $skipHandling required for paged search when cookies to
1064
+     * prior results need to be gained
1065
+     * @return bool cookie validity, true if we have more pages, false otherwise.
1066
+     */
1067
+    private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) {
1068
+        $cookie = null;
1069
+        if($pagedSearchOK) {
1070
+            $cr = $this->connection->getConnectionResource();
1071
+            foreach($sr as $key => $res) {
1072
+                if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
1073
+                    $this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
1074
+                }
1075
+            }
1076
+
1077
+            //browsing through prior pages to get the cookie for the new one
1078
+            if($skipHandling) {
1079
+                return false;
1080
+            }
1081
+            // if count is bigger, then the server does not support
1082
+            // paged search. Instead, he did a normal search. We set a
1083
+            // flag here, so the callee knows how to deal with it.
1084
+            if($iFoundItems <= $limit) {
1085
+                $this->pagedSearchedSuccessful = true;
1086
+            }
1087
+        } else {
1088
+            if(!is_null($limit)) {
1089
+                \OCP\Util::writeLog('user_ldap', 'Paged search was not available', \OCP\Util::INFO);
1090
+            }
1091
+        }
1092
+        /* ++ Fixing RHDS searches with pages with zero results ++
1093 1093
 		 * Return cookie status. If we don't have more pages, with RHDS
1094 1094
 		 * cookie is null, with openldap cookie is an empty string and
1095 1095
 		 * to 386ds '0' is a valid cookie. Even if $iFoundItems == 0
1096 1096
 		 */
1097
-		return !empty($cookie) || $cookie === '0';
1098
-	}
1099
-
1100
-	/**
1101
-	 * executes an LDAP search, but counts the results only
1102
-	 * @param string $filter the LDAP filter for the search
1103
-	 * @param array $base an array containing the LDAP subtree(s) that shall be searched
1104
-	 * @param string|string[] $attr optional, array, one or more attributes that shall be
1105
-	 * retrieved. Results will according to the order in the array.
1106
-	 * @param int $limit optional, maximum results to be counted
1107
-	 * @param int $offset optional, a starting point
1108
-	 * @param bool $skipHandling indicates whether the pages search operation is
1109
-	 * completed
1110
-	 * @return int|false Integer or false if the search could not be initialized
1111
-	 *
1112
-	 */
1113
-	private function count($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1114
-		\OCP\Util::writeLog('user_ldap', 'Count filter:  '.print_r($filter, true), \OCP\Util::DEBUG);
1115
-
1116
-		$limitPerPage = intval($this->connection->ldapPagingSize);
1117
-		if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1118
-			$limitPerPage = $limit;
1119
-		}
1120
-
1121
-		$counter = 0;
1122
-		$count = null;
1123
-		$this->connection->getConnectionResource();
1124
-
1125
-		do {
1126
-			$search = $this->executeSearch($filter, $base, $attr,
1127
-										   $limitPerPage, $offset);
1128
-			if($search === false) {
1129
-				return $counter > 0 ? $counter : false;
1130
-			}
1131
-			list($sr, $pagedSearchOK) = $search;
1132
-
1133
-			/* ++ Fixing RHDS searches with pages with zero results ++
1097
+        return !empty($cookie) || $cookie === '0';
1098
+    }
1099
+
1100
+    /**
1101
+     * executes an LDAP search, but counts the results only
1102
+     * @param string $filter the LDAP filter for the search
1103
+     * @param array $base an array containing the LDAP subtree(s) that shall be searched
1104
+     * @param string|string[] $attr optional, array, one or more attributes that shall be
1105
+     * retrieved. Results will according to the order in the array.
1106
+     * @param int $limit optional, maximum results to be counted
1107
+     * @param int $offset optional, a starting point
1108
+     * @param bool $skipHandling indicates whether the pages search operation is
1109
+     * completed
1110
+     * @return int|false Integer or false if the search could not be initialized
1111
+     *
1112
+     */
1113
+    private function count($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1114
+        \OCP\Util::writeLog('user_ldap', 'Count filter:  '.print_r($filter, true), \OCP\Util::DEBUG);
1115
+
1116
+        $limitPerPage = intval($this->connection->ldapPagingSize);
1117
+        if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1118
+            $limitPerPage = $limit;
1119
+        }
1120
+
1121
+        $counter = 0;
1122
+        $count = null;
1123
+        $this->connection->getConnectionResource();
1124
+
1125
+        do {
1126
+            $search = $this->executeSearch($filter, $base, $attr,
1127
+                                            $limitPerPage, $offset);
1128
+            if($search === false) {
1129
+                return $counter > 0 ? $counter : false;
1130
+            }
1131
+            list($sr, $pagedSearchOK) = $search;
1132
+
1133
+            /* ++ Fixing RHDS searches with pages with zero results ++
1134 1134
 			 * countEntriesInSearchResults() method signature changed
1135 1135
 			 * by removing $limit and &$hasHitLimit parameters
1136 1136
 			 */
1137
-			$count = $this->countEntriesInSearchResults($sr);
1138
-			$counter += $count;
1137
+            $count = $this->countEntriesInSearchResults($sr);
1138
+            $counter += $count;
1139 1139
 
1140
-			$hasMorePages = $this->processPagedSearchStatus($sr, $filter, $base, $count, $limitPerPage,
1141
-										$offset, $pagedSearchOK, $skipHandling);
1142
-			$offset += $limitPerPage;
1143
-			/* ++ Fixing RHDS searches with pages with zero results ++
1140
+            $hasMorePages = $this->processPagedSearchStatus($sr, $filter, $base, $count, $limitPerPage,
1141
+                                        $offset, $pagedSearchOK, $skipHandling);
1142
+            $offset += $limitPerPage;
1143
+            /* ++ Fixing RHDS searches with pages with zero results ++
1144 1144
 			 * Continue now depends on $hasMorePages value
1145 1145
 			 */
1146
-			$continue = $pagedSearchOK && $hasMorePages;
1147
-		} while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
1148
-
1149
-		return $counter;
1150
-	}
1151
-
1152
-	/**
1153
-	 * @param array $searchResults
1154
-	 * @return int
1155
-	 */
1156
-	private function countEntriesInSearchResults($searchResults) {
1157
-		$counter = 0;
1158
-
1159
-		foreach($searchResults as $res) {
1160
-			$count = intval($this->invokeLDAPMethod('countEntries', $this->connection->getConnectionResource(), $res));
1161
-			$counter += $count;
1162
-		}
1163
-
1164
-		return $counter;
1165
-	}
1166
-
1167
-	/**
1168
-	 * Executes an LDAP search
1169
-	 * @param string $filter the LDAP filter for the search
1170
-	 * @param array $base an array containing the LDAP subtree(s) that shall be searched
1171
-	 * @param string|string[] $attr optional, array, one or more attributes that shall be
1172
-	 * @param int $limit
1173
-	 * @param int $offset
1174
-	 * @param bool $skipHandling
1175
-	 * @return array with the search result
1176
-	 */
1177
-	public function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1178
-		$limitPerPage = intval($this->connection->ldapPagingSize);
1179
-		if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1180
-			$limitPerPage = $limit;
1181
-		}
1182
-
1183
-		/* ++ Fixing RHDS searches with pages with zero results ++
1146
+            $continue = $pagedSearchOK && $hasMorePages;
1147
+        } while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
1148
+
1149
+        return $counter;
1150
+    }
1151
+
1152
+    /**
1153
+     * @param array $searchResults
1154
+     * @return int
1155
+     */
1156
+    private function countEntriesInSearchResults($searchResults) {
1157
+        $counter = 0;
1158
+
1159
+        foreach($searchResults as $res) {
1160
+            $count = intval($this->invokeLDAPMethod('countEntries', $this->connection->getConnectionResource(), $res));
1161
+            $counter += $count;
1162
+        }
1163
+
1164
+        return $counter;
1165
+    }
1166
+
1167
+    /**
1168
+     * Executes an LDAP search
1169
+     * @param string $filter the LDAP filter for the search
1170
+     * @param array $base an array containing the LDAP subtree(s) that shall be searched
1171
+     * @param string|string[] $attr optional, array, one or more attributes that shall be
1172
+     * @param int $limit
1173
+     * @param int $offset
1174
+     * @param bool $skipHandling
1175
+     * @return array with the search result
1176
+     */
1177
+    public function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1178
+        $limitPerPage = intval($this->connection->ldapPagingSize);
1179
+        if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1180
+            $limitPerPage = $limit;
1181
+        }
1182
+
1183
+        /* ++ Fixing RHDS searches with pages with zero results ++
1184 1184
 		 * As we can have pages with zero results and/or pages with less
1185 1185
 		 * than $limit results but with a still valid server 'cookie',
1186 1186
 		 * loops through until we get $continue equals true and
1187 1187
 		 * $findings['count'] < $limit
1188 1188
 		 */
1189
-		$findings = array();
1190
-		$savedoffset = $offset;
1191
-		do {
1192
-			$search = $this->executeSearch($filter, $base, $attr, $limitPerPage, $offset);
1193
-			if($search === false) {
1194
-				return array();
1195
-			}
1196
-			list($sr, $pagedSearchOK) = $search;
1197
-			$cr = $this->connection->getConnectionResource();
1198
-
1199
-			if($skipHandling) {
1200
-				//i.e. result do not need to be fetched, we just need the cookie
1201
-				//thus pass 1 or any other value as $iFoundItems because it is not
1202
-				//used
1203
-				$this->processPagedSearchStatus($sr, $filter, $base, 1, $limitPerPage,
1204
-								$offset, $pagedSearchOK,
1205
-								$skipHandling);
1206
-				return array();
1207
-			}
1208
-
1209
-			$iFoundItems = 0;
1210
-			foreach($sr as $res) {
1211
-				$findings = array_merge($findings, $this->invokeLDAPMethod('getEntries', $cr, $res));
1212
-				$iFoundItems = max($iFoundItems, $findings['count']);
1213
-				unset($findings['count']);
1214
-			}
1215
-
1216
-			$continue = $this->processPagedSearchStatus($sr, $filter, $base, $iFoundItems,
1217
-				$limitPerPage, $offset, $pagedSearchOK,
1218
-										$skipHandling);
1219
-			$offset += $limitPerPage;
1220
-		} while ($continue && $pagedSearchOK && ($limit === null || count($findings) < $limit));
1221
-		// reseting offset
1222
-		$offset = $savedoffset;
1223
-
1224
-		// if we're here, probably no connection resource is returned.
1225
-		// to make Nextcloud behave nicely, we simply give back an empty array.
1226
-		if(is_null($findings)) {
1227
-			return array();
1228
-		}
1229
-
1230
-		if(!is_null($attr)) {
1231
-			$selection = array();
1232
-			$i = 0;
1233
-			foreach($findings as $item) {
1234
-				if(!is_array($item)) {
1235
-					continue;
1236
-				}
1237
-				$item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1238
-				foreach($attr as $key) {
1239
-					$key = mb_strtolower($key, 'UTF-8');
1240
-					if(isset($item[$key])) {
1241
-						if(is_array($item[$key]) && isset($item[$key]['count'])) {
1242
-							unset($item[$key]['count']);
1243
-						}
1244
-						if($key !== 'dn') {
1245
-							$selection[$i][$key] = $this->resemblesDN($key) ?
1246
-								$this->helper->sanitizeDN($item[$key])
1247
-								: $key === 'objectguid' || $key === 'guid' ?
1248
-									$selection[$i][$key] = $this->convertObjectGUID2Str($item[$key])
1249
-									: $item[$key];
1250
-						} else {
1251
-							$selection[$i][$key] = [$this->helper->sanitizeDN($item[$key])];
1252
-						}
1253
-					}
1254
-
1255
-				}
1256
-				$i++;
1257
-			}
1258
-			$findings = $selection;
1259
-		}
1260
-		//we slice the findings, when
1261
-		//a) paged search unsuccessful, though attempted
1262
-		//b) no paged search, but limit set
1263
-		if((!$this->getPagedSearchResultState()
1264
-			&& $pagedSearchOK)
1265
-			|| (
1266
-				!$pagedSearchOK
1267
-				&& !is_null($limit)
1268
-			)
1269
-		) {
1270
-			$findings = array_slice($findings, intval($offset), $limit);
1271
-		}
1272
-		return $findings;
1273
-	}
1274
-
1275
-	/**
1276
-	 * @param string $name
1277
-	 * @return bool|mixed|string
1278
-	 */
1279
-	public function sanitizeUsername($name) {
1280
-		if($this->connection->ldapIgnoreNamingRules) {
1281
-			return $name;
1282
-		}
1283
-
1284
-		// Transliteration
1285
-		// latin characters to ASCII
1286
-		$name = iconv('UTF-8', 'ASCII//TRANSLIT', $name);
1287
-
1288
-		// Replacements
1289
-		$name = str_replace(' ', '_', $name);
1290
-
1291
-		// Every remaining disallowed characters will be removed
1292
-		$name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name);
1293
-
1294
-		return $name;
1295
-	}
1296
-
1297
-	/**
1298
-	* escapes (user provided) parts for LDAP filter
1299
-	* @param string $input, the provided value
1300
-	* @param bool $allowAsterisk whether in * at the beginning should be preserved
1301
-	* @return string the escaped string
1302
-	*/
1303
-	public function escapeFilterPart($input, $allowAsterisk = false) {
1304
-		$asterisk = '';
1305
-		if($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1306
-			$asterisk = '*';
1307
-			$input = mb_substr($input, 1, null, 'UTF-8');
1308
-		}
1309
-		$search  = array('*', '\\', '(', ')');
1310
-		$replace = array('\\*', '\\\\', '\\(', '\\)');
1311
-		return $asterisk . str_replace($search, $replace, $input);
1312
-	}
1313
-
1314
-	/**
1315
-	 * combines the input filters with AND
1316
-	 * @param string[] $filters the filters to connect
1317
-	 * @return string the combined filter
1318
-	 */
1319
-	public function combineFilterWithAnd($filters) {
1320
-		return $this->combineFilter($filters, '&');
1321
-	}
1322
-
1323
-	/**
1324
-	 * combines the input filters with OR
1325
-	 * @param string[] $filters the filters to connect
1326
-	 * @return string the combined filter
1327
-	 * Combines Filter arguments with OR
1328
-	 */
1329
-	public function combineFilterWithOr($filters) {
1330
-		return $this->combineFilter($filters, '|');
1331
-	}
1332
-
1333
-	/**
1334
-	 * combines the input filters with given operator
1335
-	 * @param string[] $filters the filters to connect
1336
-	 * @param string $operator either & or |
1337
-	 * @return string the combined filter
1338
-	 */
1339
-	private function combineFilter($filters, $operator) {
1340
-		$combinedFilter = '('.$operator;
1341
-		foreach($filters as $filter) {
1342
-			if ($filter !== '' && $filter[0] !== '(') {
1343
-				$filter = '('.$filter.')';
1344
-			}
1345
-			$combinedFilter.=$filter;
1346
-		}
1347
-		$combinedFilter.=')';
1348
-		return $combinedFilter;
1349
-	}
1350
-
1351
-	/**
1352
-	 * creates a filter part for to perform search for users
1353
-	 * @param string $search the search term
1354
-	 * @return string the final filter part to use in LDAP searches
1355
-	 */
1356
-	public function getFilterPartForUserSearch($search) {
1357
-		return $this->getFilterPartForSearch($search,
1358
-			$this->connection->ldapAttributesForUserSearch,
1359
-			$this->connection->ldapUserDisplayName);
1360
-	}
1361
-
1362
-	/**
1363
-	 * creates a filter part for to perform search for groups
1364
-	 * @param string $search the search term
1365
-	 * @return string the final filter part to use in LDAP searches
1366
-	 */
1367
-	public function getFilterPartForGroupSearch($search) {
1368
-		return $this->getFilterPartForSearch($search,
1369
-			$this->connection->ldapAttributesForGroupSearch,
1370
-			$this->connection->ldapGroupDisplayName);
1371
-	}
1372
-
1373
-	/**
1374
-	 * creates a filter part for searches by splitting up the given search
1375
-	 * string into single words
1376
-	 * @param string $search the search term
1377
-	 * @param string[] $searchAttributes needs to have at least two attributes,
1378
-	 * otherwise it does not make sense :)
1379
-	 * @return string the final filter part to use in LDAP searches
1380
-	 * @throws \Exception
1381
-	 */
1382
-	private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1383
-		if(!is_array($searchAttributes) || count($searchAttributes) < 2) {
1384
-			throw new \Exception('searchAttributes must be an array with at least two string');
1385
-		}
1386
-		$searchWords = explode(' ', trim($search));
1387
-		$wordFilters = array();
1388
-		foreach($searchWords as $word) {
1389
-			$word = $this->prepareSearchTerm($word);
1390
-			//every word needs to appear at least once
1391
-			$wordMatchOneAttrFilters = array();
1392
-			foreach($searchAttributes as $attr) {
1393
-				$wordMatchOneAttrFilters[] = $attr . '=' . $word;
1394
-			}
1395
-			$wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1396
-		}
1397
-		return $this->combineFilterWithAnd($wordFilters);
1398
-	}
1399
-
1400
-	/**
1401
-	 * creates a filter part for searches
1402
-	 * @param string $search the search term
1403
-	 * @param string[]|null $searchAttributes
1404
-	 * @param string $fallbackAttribute a fallback attribute in case the user
1405
-	 * did not define search attributes. Typically the display name attribute.
1406
-	 * @return string the final filter part to use in LDAP searches
1407
-	 */
1408
-	private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1409
-		$filter = array();
1410
-		$haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1411
-		if($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1412
-			try {
1413
-				return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1414
-			} catch(\Exception $e) {
1415
-				\OCP\Util::writeLog(
1416
-					'user_ldap',
1417
-					'Creating advanced filter for search failed, falling back to simple method.',
1418
-					\OCP\Util::INFO
1419
-				);
1420
-			}
1421
-		}
1422
-
1423
-		$search = $this->prepareSearchTerm($search);
1424
-		if(!is_array($searchAttributes) || count($searchAttributes) === 0) {
1425
-			if ($fallbackAttribute === '') {
1426
-				return '';
1427
-			}
1428
-			$filter[] = $fallbackAttribute . '=' . $search;
1429
-		} else {
1430
-			foreach($searchAttributes as $attribute) {
1431
-				$filter[] = $attribute . '=' . $search;
1432
-			}
1433
-		}
1434
-		if(count($filter) === 1) {
1435
-			return '('.$filter[0].')';
1436
-		}
1437
-		return $this->combineFilterWithOr($filter);
1438
-	}
1439
-
1440
-	/**
1441
-	 * returns the search term depending on whether we are allowed
1442
-	 * list users found by ldap with the current input appended by
1443
-	 * a *
1444
-	 * @return string
1445
-	 */
1446
-	private function prepareSearchTerm($term) {
1447
-		$config = \OC::$server->getConfig();
1448
-
1449
-		$allowEnum = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes');
1450
-
1451
-		$result = $term;
1452
-		if ($term === '') {
1453
-			$result = '*';
1454
-		} else if ($allowEnum !== 'no') {
1455
-			$result = $term . '*';
1456
-		}
1457
-		return $result;
1458
-	}
1459
-
1460
-	/**
1461
-	 * returns the filter used for counting users
1462
-	 * @return string
1463
-	 */
1464
-	public function getFilterForUserCount() {
1465
-		$filter = $this->combineFilterWithAnd(array(
1466
-			$this->connection->ldapUserFilter,
1467
-			$this->connection->ldapUserDisplayName . '=*'
1468
-		));
1469
-
1470
-		return $filter;
1471
-	}
1472
-
1473
-	/**
1474
-	 * @param string $name
1475
-	 * @param string $password
1476
-	 * @return bool
1477
-	 */
1478
-	public function areCredentialsValid($name, $password) {
1479
-		$name = $this->helper->DNasBaseParameter($name);
1480
-		$testConnection = clone $this->connection;
1481
-		$credentials = array(
1482
-			'ldapAgentName' => $name,
1483
-			'ldapAgentPassword' => $password
1484
-		);
1485
-		if(!$testConnection->setConfiguration($credentials)) {
1486
-			return false;
1487
-		}
1488
-		return $testConnection->bind();
1489
-	}
1490
-
1491
-	/**
1492
-	 * reverse lookup of a DN given a known UUID
1493
-	 *
1494
-	 * @param string $uuid
1495
-	 * @return string
1496
-	 * @throws \Exception
1497
-	 */
1498
-	public function getUserDnByUuid($uuid) {
1499
-		$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1500
-		$filter       = $this->connection->ldapUserFilter;
1501
-		$base         = $this->connection->ldapBaseUsers;
1502
-
1503
-		if ($this->connection->ldapUuidUserAttribute === 'auto' && $uuidOverride === '') {
1504
-			// Sacrebleu! The UUID attribute is unknown :( We need first an
1505
-			// existing DN to be able to reliably detect it.
1506
-			$result = $this->search($filter, $base, ['dn'], 1);
1507
-			if(!isset($result[0]) || !isset($result[0]['dn'])) {
1508
-				throw new \Exception('Cannot determine UUID attribute');
1509
-			}
1510
-			$dn = $result[0]['dn'][0];
1511
-			if(!$this->detectUuidAttribute($dn, true)) {
1512
-				throw new \Exception('Cannot determine UUID attribute');
1513
-			}
1514
-		} else {
1515
-			// The UUID attribute is either known or an override is given.
1516
-			// By calling this method we ensure that $this->connection->$uuidAttr
1517
-			// is definitely set
1518
-			if(!$this->detectUuidAttribute('', true)) {
1519
-				throw new \Exception('Cannot determine UUID attribute');
1520
-			}
1521
-		}
1522
-
1523
-		$uuidAttr = $this->connection->ldapUuidUserAttribute;
1524
-		if($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1525
-			$uuid = $this->formatGuid2ForFilterUser($uuid);
1526
-		}
1527
-
1528
-		$filter = $uuidAttr . '=' . $uuid;
1529
-		$result = $this->searchUsers($filter, ['dn'], 2);
1530
-		if(is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1531
-			// we put the count into account to make sure that this is
1532
-			// really unique
1533
-			return $result[0]['dn'][0];
1534
-		}
1535
-
1536
-		throw new \Exception('Cannot determine UUID attribute');
1537
-	}
1538
-
1539
-	/**
1540
-	 * auto-detects the directory's UUID attribute
1541
-	 *
1542
-	 * @param string $dn a known DN used to check against
1543
-	 * @param bool $isUser
1544
-	 * @param bool $force the detection should be run, even if it is not set to auto
1545
-	 * @param array|null $ldapRecord
1546
-	 * @return bool true on success, false otherwise
1547
-	 */
1548
-	private function detectUuidAttribute($dn, $isUser = true, $force = false, array $ldapRecord = null) {
1549
-		if($isUser) {
1550
-			$uuidAttr     = 'ldapUuidUserAttribute';
1551
-			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1552
-		} else {
1553
-			$uuidAttr     = 'ldapUuidGroupAttribute';
1554
-			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1555
-		}
1556
-
1557
-		if(($this->connection->$uuidAttr !== 'auto') && !$force) {
1558
-			return true;
1559
-		}
1560
-
1561
-		if (is_string($uuidOverride) && trim($uuidOverride) !== '' && !$force) {
1562
-			$this->connection->$uuidAttr = $uuidOverride;
1563
-			return true;
1564
-		}
1565
-
1566
-		foreach(self::UUID_ATTRIBUTES as $attribute) {
1567
-			if($ldapRecord !== null) {
1568
-				// we have the info from LDAP already, we don't need to talk to the server again
1569
-				if(isset($ldapRecord[$attribute])) {
1570
-					$this->connection->$uuidAttr = $attribute;
1571
-					return true;
1572
-				} else {
1573
-					continue;
1574
-				}
1575
-			}
1576
-
1577
-			$value = $this->readAttribute($dn, $attribute);
1578
-			if(is_array($value) && isset($value[0]) && !empty($value[0])) {
1579
-				\OCP\Util::writeLog('user_ldap',
1580
-									'Setting '.$attribute.' as '.$uuidAttr,
1581
-									\OCP\Util::DEBUG);
1582
-				$this->connection->$uuidAttr = $attribute;
1583
-				return true;
1584
-			}
1585
-		}
1586
-		\OCP\Util::writeLog('user_ldap',
1587
-							'Could not autodetect the UUID attribute',
1588
-							\OCP\Util::ERROR);
1589
-
1590
-		return false;
1591
-	}
1592
-
1593
-	/**
1594
-	 * @param string $dn
1595
-	 * @param bool $isUser
1596
-	 * @param null $ldapRecord
1597
-	 * @return bool|string
1598
-	 */
1599
-	public function getUUID($dn, $isUser = true, $ldapRecord = null) {
1600
-		if($isUser) {
1601
-			$uuidAttr     = 'ldapUuidUserAttribute';
1602
-			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1603
-		} else {
1604
-			$uuidAttr     = 'ldapUuidGroupAttribute';
1605
-			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1606
-		}
1607
-
1608
-		$uuid = false;
1609
-		if($this->detectUuidAttribute($dn, $isUser, false, $ldapRecord)) {
1610
-			$attr = $this->connection->$uuidAttr;
1611
-			$uuid = isset($ldapRecord[$attr]) ? $ldapRecord[$attr] : $this->readAttribute($dn, $attr);
1612
-			if( !is_array($uuid)
1613
-				&& $uuidOverride !== ''
1614
-				&& $this->detectUuidAttribute($dn, $isUser, true, $ldapRecord))
1615
-			{
1616
-				$uuid = isset($ldapRecord[$this->connection->$uuidAttr])
1617
-					? $ldapRecord[$this->connection->$uuidAttr]
1618
-					: $this->readAttribute($dn, $this->connection->$uuidAttr);
1619
-			}
1620
-			if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1621
-				$uuid = $uuid[0];
1622
-			}
1623
-		}
1624
-
1625
-		return $uuid;
1626
-	}
1627
-
1628
-	/**
1629
-	 * converts a binary ObjectGUID into a string representation
1630
-	 * @param string $oguid the ObjectGUID in it's binary form as retrieved from AD
1631
-	 * @return string
1632
-	 * @link http://www.php.net/manual/en/function.ldap-get-values-len.php#73198
1633
-	 */
1634
-	private function convertObjectGUID2Str($oguid) {
1635
-		$hex_guid = bin2hex($oguid);
1636
-		$hex_guid_to_guid_str = '';
1637
-		for($k = 1; $k <= 4; ++$k) {
1638
-			$hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1639
-		}
1640
-		$hex_guid_to_guid_str .= '-';
1641
-		for($k = 1; $k <= 2; ++$k) {
1642
-			$hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1643
-		}
1644
-		$hex_guid_to_guid_str .= '-';
1645
-		for($k = 1; $k <= 2; ++$k) {
1646
-			$hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1647
-		}
1648
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1649
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1650
-
1651
-		return strtoupper($hex_guid_to_guid_str);
1652
-	}
1653
-
1654
-	/**
1655
-	 * the first three blocks of the string-converted GUID happen to be in
1656
-	 * reverse order. In order to use it in a filter, this needs to be
1657
-	 * corrected. Furthermore the dashes need to be replaced and \\ preprended
1658
-	 * to every two hax figures.
1659
-	 *
1660
-	 * If an invalid string is passed, it will be returned without change.
1661
-	 *
1662
-	 * @param string $guid
1663
-	 * @return string
1664
-	 */
1665
-	public function formatGuid2ForFilterUser($guid) {
1666
-		if(!is_string($guid)) {
1667
-			throw new \InvalidArgumentException('String expected');
1668
-		}
1669
-		$blocks = explode('-', $guid);
1670
-		if(count($blocks) !== 5) {
1671
-			/*
1189
+        $findings = array();
1190
+        $savedoffset = $offset;
1191
+        do {
1192
+            $search = $this->executeSearch($filter, $base, $attr, $limitPerPage, $offset);
1193
+            if($search === false) {
1194
+                return array();
1195
+            }
1196
+            list($sr, $pagedSearchOK) = $search;
1197
+            $cr = $this->connection->getConnectionResource();
1198
+
1199
+            if($skipHandling) {
1200
+                //i.e. result do not need to be fetched, we just need the cookie
1201
+                //thus pass 1 or any other value as $iFoundItems because it is not
1202
+                //used
1203
+                $this->processPagedSearchStatus($sr, $filter, $base, 1, $limitPerPage,
1204
+                                $offset, $pagedSearchOK,
1205
+                                $skipHandling);
1206
+                return array();
1207
+            }
1208
+
1209
+            $iFoundItems = 0;
1210
+            foreach($sr as $res) {
1211
+                $findings = array_merge($findings, $this->invokeLDAPMethod('getEntries', $cr, $res));
1212
+                $iFoundItems = max($iFoundItems, $findings['count']);
1213
+                unset($findings['count']);
1214
+            }
1215
+
1216
+            $continue = $this->processPagedSearchStatus($sr, $filter, $base, $iFoundItems,
1217
+                $limitPerPage, $offset, $pagedSearchOK,
1218
+                                        $skipHandling);
1219
+            $offset += $limitPerPage;
1220
+        } while ($continue && $pagedSearchOK && ($limit === null || count($findings) < $limit));
1221
+        // reseting offset
1222
+        $offset = $savedoffset;
1223
+
1224
+        // if we're here, probably no connection resource is returned.
1225
+        // to make Nextcloud behave nicely, we simply give back an empty array.
1226
+        if(is_null($findings)) {
1227
+            return array();
1228
+        }
1229
+
1230
+        if(!is_null($attr)) {
1231
+            $selection = array();
1232
+            $i = 0;
1233
+            foreach($findings as $item) {
1234
+                if(!is_array($item)) {
1235
+                    continue;
1236
+                }
1237
+                $item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1238
+                foreach($attr as $key) {
1239
+                    $key = mb_strtolower($key, 'UTF-8');
1240
+                    if(isset($item[$key])) {
1241
+                        if(is_array($item[$key]) && isset($item[$key]['count'])) {
1242
+                            unset($item[$key]['count']);
1243
+                        }
1244
+                        if($key !== 'dn') {
1245
+                            $selection[$i][$key] = $this->resemblesDN($key) ?
1246
+                                $this->helper->sanitizeDN($item[$key])
1247
+                                : $key === 'objectguid' || $key === 'guid' ?
1248
+                                    $selection[$i][$key] = $this->convertObjectGUID2Str($item[$key])
1249
+                                    : $item[$key];
1250
+                        } else {
1251
+                            $selection[$i][$key] = [$this->helper->sanitizeDN($item[$key])];
1252
+                        }
1253
+                    }
1254
+
1255
+                }
1256
+                $i++;
1257
+            }
1258
+            $findings = $selection;
1259
+        }
1260
+        //we slice the findings, when
1261
+        //a) paged search unsuccessful, though attempted
1262
+        //b) no paged search, but limit set
1263
+        if((!$this->getPagedSearchResultState()
1264
+            && $pagedSearchOK)
1265
+            || (
1266
+                !$pagedSearchOK
1267
+                && !is_null($limit)
1268
+            )
1269
+        ) {
1270
+            $findings = array_slice($findings, intval($offset), $limit);
1271
+        }
1272
+        return $findings;
1273
+    }
1274
+
1275
+    /**
1276
+     * @param string $name
1277
+     * @return bool|mixed|string
1278
+     */
1279
+    public function sanitizeUsername($name) {
1280
+        if($this->connection->ldapIgnoreNamingRules) {
1281
+            return $name;
1282
+        }
1283
+
1284
+        // Transliteration
1285
+        // latin characters to ASCII
1286
+        $name = iconv('UTF-8', 'ASCII//TRANSLIT', $name);
1287
+
1288
+        // Replacements
1289
+        $name = str_replace(' ', '_', $name);
1290
+
1291
+        // Every remaining disallowed characters will be removed
1292
+        $name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name);
1293
+
1294
+        return $name;
1295
+    }
1296
+
1297
+    /**
1298
+     * escapes (user provided) parts for LDAP filter
1299
+     * @param string $input, the provided value
1300
+     * @param bool $allowAsterisk whether in * at the beginning should be preserved
1301
+     * @return string the escaped string
1302
+     */
1303
+    public function escapeFilterPart($input, $allowAsterisk = false) {
1304
+        $asterisk = '';
1305
+        if($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1306
+            $asterisk = '*';
1307
+            $input = mb_substr($input, 1, null, 'UTF-8');
1308
+        }
1309
+        $search  = array('*', '\\', '(', ')');
1310
+        $replace = array('\\*', '\\\\', '\\(', '\\)');
1311
+        return $asterisk . str_replace($search, $replace, $input);
1312
+    }
1313
+
1314
+    /**
1315
+     * combines the input filters with AND
1316
+     * @param string[] $filters the filters to connect
1317
+     * @return string the combined filter
1318
+     */
1319
+    public function combineFilterWithAnd($filters) {
1320
+        return $this->combineFilter($filters, '&');
1321
+    }
1322
+
1323
+    /**
1324
+     * combines the input filters with OR
1325
+     * @param string[] $filters the filters to connect
1326
+     * @return string the combined filter
1327
+     * Combines Filter arguments with OR
1328
+     */
1329
+    public function combineFilterWithOr($filters) {
1330
+        return $this->combineFilter($filters, '|');
1331
+    }
1332
+
1333
+    /**
1334
+     * combines the input filters with given operator
1335
+     * @param string[] $filters the filters to connect
1336
+     * @param string $operator either & or |
1337
+     * @return string the combined filter
1338
+     */
1339
+    private function combineFilter($filters, $operator) {
1340
+        $combinedFilter = '('.$operator;
1341
+        foreach($filters as $filter) {
1342
+            if ($filter !== '' && $filter[0] !== '(') {
1343
+                $filter = '('.$filter.')';
1344
+            }
1345
+            $combinedFilter.=$filter;
1346
+        }
1347
+        $combinedFilter.=')';
1348
+        return $combinedFilter;
1349
+    }
1350
+
1351
+    /**
1352
+     * creates a filter part for to perform search for users
1353
+     * @param string $search the search term
1354
+     * @return string the final filter part to use in LDAP searches
1355
+     */
1356
+    public function getFilterPartForUserSearch($search) {
1357
+        return $this->getFilterPartForSearch($search,
1358
+            $this->connection->ldapAttributesForUserSearch,
1359
+            $this->connection->ldapUserDisplayName);
1360
+    }
1361
+
1362
+    /**
1363
+     * creates a filter part for to perform search for groups
1364
+     * @param string $search the search term
1365
+     * @return string the final filter part to use in LDAP searches
1366
+     */
1367
+    public function getFilterPartForGroupSearch($search) {
1368
+        return $this->getFilterPartForSearch($search,
1369
+            $this->connection->ldapAttributesForGroupSearch,
1370
+            $this->connection->ldapGroupDisplayName);
1371
+    }
1372
+
1373
+    /**
1374
+     * creates a filter part for searches by splitting up the given search
1375
+     * string into single words
1376
+     * @param string $search the search term
1377
+     * @param string[] $searchAttributes needs to have at least two attributes,
1378
+     * otherwise it does not make sense :)
1379
+     * @return string the final filter part to use in LDAP searches
1380
+     * @throws \Exception
1381
+     */
1382
+    private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1383
+        if(!is_array($searchAttributes) || count($searchAttributes) < 2) {
1384
+            throw new \Exception('searchAttributes must be an array with at least two string');
1385
+        }
1386
+        $searchWords = explode(' ', trim($search));
1387
+        $wordFilters = array();
1388
+        foreach($searchWords as $word) {
1389
+            $word = $this->prepareSearchTerm($word);
1390
+            //every word needs to appear at least once
1391
+            $wordMatchOneAttrFilters = array();
1392
+            foreach($searchAttributes as $attr) {
1393
+                $wordMatchOneAttrFilters[] = $attr . '=' . $word;
1394
+            }
1395
+            $wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1396
+        }
1397
+        return $this->combineFilterWithAnd($wordFilters);
1398
+    }
1399
+
1400
+    /**
1401
+     * creates a filter part for searches
1402
+     * @param string $search the search term
1403
+     * @param string[]|null $searchAttributes
1404
+     * @param string $fallbackAttribute a fallback attribute in case the user
1405
+     * did not define search attributes. Typically the display name attribute.
1406
+     * @return string the final filter part to use in LDAP searches
1407
+     */
1408
+    private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1409
+        $filter = array();
1410
+        $haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1411
+        if($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1412
+            try {
1413
+                return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1414
+            } catch(\Exception $e) {
1415
+                \OCP\Util::writeLog(
1416
+                    'user_ldap',
1417
+                    'Creating advanced filter for search failed, falling back to simple method.',
1418
+                    \OCP\Util::INFO
1419
+                );
1420
+            }
1421
+        }
1422
+
1423
+        $search = $this->prepareSearchTerm($search);
1424
+        if(!is_array($searchAttributes) || count($searchAttributes) === 0) {
1425
+            if ($fallbackAttribute === '') {
1426
+                return '';
1427
+            }
1428
+            $filter[] = $fallbackAttribute . '=' . $search;
1429
+        } else {
1430
+            foreach($searchAttributes as $attribute) {
1431
+                $filter[] = $attribute . '=' . $search;
1432
+            }
1433
+        }
1434
+        if(count($filter) === 1) {
1435
+            return '('.$filter[0].')';
1436
+        }
1437
+        return $this->combineFilterWithOr($filter);
1438
+    }
1439
+
1440
+    /**
1441
+     * returns the search term depending on whether we are allowed
1442
+     * list users found by ldap with the current input appended by
1443
+     * a *
1444
+     * @return string
1445
+     */
1446
+    private function prepareSearchTerm($term) {
1447
+        $config = \OC::$server->getConfig();
1448
+
1449
+        $allowEnum = $config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes');
1450
+
1451
+        $result = $term;
1452
+        if ($term === '') {
1453
+            $result = '*';
1454
+        } else if ($allowEnum !== 'no') {
1455
+            $result = $term . '*';
1456
+        }
1457
+        return $result;
1458
+    }
1459
+
1460
+    /**
1461
+     * returns the filter used for counting users
1462
+     * @return string
1463
+     */
1464
+    public function getFilterForUserCount() {
1465
+        $filter = $this->combineFilterWithAnd(array(
1466
+            $this->connection->ldapUserFilter,
1467
+            $this->connection->ldapUserDisplayName . '=*'
1468
+        ));
1469
+
1470
+        return $filter;
1471
+    }
1472
+
1473
+    /**
1474
+     * @param string $name
1475
+     * @param string $password
1476
+     * @return bool
1477
+     */
1478
+    public function areCredentialsValid($name, $password) {
1479
+        $name = $this->helper->DNasBaseParameter($name);
1480
+        $testConnection = clone $this->connection;
1481
+        $credentials = array(
1482
+            'ldapAgentName' => $name,
1483
+            'ldapAgentPassword' => $password
1484
+        );
1485
+        if(!$testConnection->setConfiguration($credentials)) {
1486
+            return false;
1487
+        }
1488
+        return $testConnection->bind();
1489
+    }
1490
+
1491
+    /**
1492
+     * reverse lookup of a DN given a known UUID
1493
+     *
1494
+     * @param string $uuid
1495
+     * @return string
1496
+     * @throws \Exception
1497
+     */
1498
+    public function getUserDnByUuid($uuid) {
1499
+        $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1500
+        $filter       = $this->connection->ldapUserFilter;
1501
+        $base         = $this->connection->ldapBaseUsers;
1502
+
1503
+        if ($this->connection->ldapUuidUserAttribute === 'auto' && $uuidOverride === '') {
1504
+            // Sacrebleu! The UUID attribute is unknown :( We need first an
1505
+            // existing DN to be able to reliably detect it.
1506
+            $result = $this->search($filter, $base, ['dn'], 1);
1507
+            if(!isset($result[0]) || !isset($result[0]['dn'])) {
1508
+                throw new \Exception('Cannot determine UUID attribute');
1509
+            }
1510
+            $dn = $result[0]['dn'][0];
1511
+            if(!$this->detectUuidAttribute($dn, true)) {
1512
+                throw new \Exception('Cannot determine UUID attribute');
1513
+            }
1514
+        } else {
1515
+            // The UUID attribute is either known or an override is given.
1516
+            // By calling this method we ensure that $this->connection->$uuidAttr
1517
+            // is definitely set
1518
+            if(!$this->detectUuidAttribute('', true)) {
1519
+                throw new \Exception('Cannot determine UUID attribute');
1520
+            }
1521
+        }
1522
+
1523
+        $uuidAttr = $this->connection->ldapUuidUserAttribute;
1524
+        if($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1525
+            $uuid = $this->formatGuid2ForFilterUser($uuid);
1526
+        }
1527
+
1528
+        $filter = $uuidAttr . '=' . $uuid;
1529
+        $result = $this->searchUsers($filter, ['dn'], 2);
1530
+        if(is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1531
+            // we put the count into account to make sure that this is
1532
+            // really unique
1533
+            return $result[0]['dn'][0];
1534
+        }
1535
+
1536
+        throw new \Exception('Cannot determine UUID attribute');
1537
+    }
1538
+
1539
+    /**
1540
+     * auto-detects the directory's UUID attribute
1541
+     *
1542
+     * @param string $dn a known DN used to check against
1543
+     * @param bool $isUser
1544
+     * @param bool $force the detection should be run, even if it is not set to auto
1545
+     * @param array|null $ldapRecord
1546
+     * @return bool true on success, false otherwise
1547
+     */
1548
+    private function detectUuidAttribute($dn, $isUser = true, $force = false, array $ldapRecord = null) {
1549
+        if($isUser) {
1550
+            $uuidAttr     = 'ldapUuidUserAttribute';
1551
+            $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1552
+        } else {
1553
+            $uuidAttr     = 'ldapUuidGroupAttribute';
1554
+            $uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1555
+        }
1556
+
1557
+        if(($this->connection->$uuidAttr !== 'auto') && !$force) {
1558
+            return true;
1559
+        }
1560
+
1561
+        if (is_string($uuidOverride) && trim($uuidOverride) !== '' && !$force) {
1562
+            $this->connection->$uuidAttr = $uuidOverride;
1563
+            return true;
1564
+        }
1565
+
1566
+        foreach(self::UUID_ATTRIBUTES as $attribute) {
1567
+            if($ldapRecord !== null) {
1568
+                // we have the info from LDAP already, we don't need to talk to the server again
1569
+                if(isset($ldapRecord[$attribute])) {
1570
+                    $this->connection->$uuidAttr = $attribute;
1571
+                    return true;
1572
+                } else {
1573
+                    continue;
1574
+                }
1575
+            }
1576
+
1577
+            $value = $this->readAttribute($dn, $attribute);
1578
+            if(is_array($value) && isset($value[0]) && !empty($value[0])) {
1579
+                \OCP\Util::writeLog('user_ldap',
1580
+                                    'Setting '.$attribute.' as '.$uuidAttr,
1581
+                                    \OCP\Util::DEBUG);
1582
+                $this->connection->$uuidAttr = $attribute;
1583
+                return true;
1584
+            }
1585
+        }
1586
+        \OCP\Util::writeLog('user_ldap',
1587
+                            'Could not autodetect the UUID attribute',
1588
+                            \OCP\Util::ERROR);
1589
+
1590
+        return false;
1591
+    }
1592
+
1593
+    /**
1594
+     * @param string $dn
1595
+     * @param bool $isUser
1596
+     * @param null $ldapRecord
1597
+     * @return bool|string
1598
+     */
1599
+    public function getUUID($dn, $isUser = true, $ldapRecord = null) {
1600
+        if($isUser) {
1601
+            $uuidAttr     = 'ldapUuidUserAttribute';
1602
+            $uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1603
+        } else {
1604
+            $uuidAttr     = 'ldapUuidGroupAttribute';
1605
+            $uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1606
+        }
1607
+
1608
+        $uuid = false;
1609
+        if($this->detectUuidAttribute($dn, $isUser, false, $ldapRecord)) {
1610
+            $attr = $this->connection->$uuidAttr;
1611
+            $uuid = isset($ldapRecord[$attr]) ? $ldapRecord[$attr] : $this->readAttribute($dn, $attr);
1612
+            if( !is_array($uuid)
1613
+                && $uuidOverride !== ''
1614
+                && $this->detectUuidAttribute($dn, $isUser, true, $ldapRecord))
1615
+            {
1616
+                $uuid = isset($ldapRecord[$this->connection->$uuidAttr])
1617
+                    ? $ldapRecord[$this->connection->$uuidAttr]
1618
+                    : $this->readAttribute($dn, $this->connection->$uuidAttr);
1619
+            }
1620
+            if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1621
+                $uuid = $uuid[0];
1622
+            }
1623
+        }
1624
+
1625
+        return $uuid;
1626
+    }
1627
+
1628
+    /**
1629
+     * converts a binary ObjectGUID into a string representation
1630
+     * @param string $oguid the ObjectGUID in it's binary form as retrieved from AD
1631
+     * @return string
1632
+     * @link http://www.php.net/manual/en/function.ldap-get-values-len.php#73198
1633
+     */
1634
+    private function convertObjectGUID2Str($oguid) {
1635
+        $hex_guid = bin2hex($oguid);
1636
+        $hex_guid_to_guid_str = '';
1637
+        for($k = 1; $k <= 4; ++$k) {
1638
+            $hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1639
+        }
1640
+        $hex_guid_to_guid_str .= '-';
1641
+        for($k = 1; $k <= 2; ++$k) {
1642
+            $hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1643
+        }
1644
+        $hex_guid_to_guid_str .= '-';
1645
+        for($k = 1; $k <= 2; ++$k) {
1646
+            $hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1647
+        }
1648
+        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1649
+        $hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1650
+
1651
+        return strtoupper($hex_guid_to_guid_str);
1652
+    }
1653
+
1654
+    /**
1655
+     * the first three blocks of the string-converted GUID happen to be in
1656
+     * reverse order. In order to use it in a filter, this needs to be
1657
+     * corrected. Furthermore the dashes need to be replaced and \\ preprended
1658
+     * to every two hax figures.
1659
+     *
1660
+     * If an invalid string is passed, it will be returned without change.
1661
+     *
1662
+     * @param string $guid
1663
+     * @return string
1664
+     */
1665
+    public function formatGuid2ForFilterUser($guid) {
1666
+        if(!is_string($guid)) {
1667
+            throw new \InvalidArgumentException('String expected');
1668
+        }
1669
+        $blocks = explode('-', $guid);
1670
+        if(count($blocks) !== 5) {
1671
+            /*
1672 1672
 			 * Why not throw an Exception instead? This method is a utility
1673 1673
 			 * called only when trying to figure out whether a "missing" known
1674 1674
 			 * LDAP user was or was not renamed on the LDAP server. And this
@@ -1679,274 +1679,274 @@  discard block
 block discarded – undo
1679 1679
 			 * an exception here would kill the experience for a valid, acting
1680 1680
 			 * user. Instead we write a log message.
1681 1681
 			 */
1682
-			\OC::$server->getLogger()->info(
1683
-				'Passed string does not resemble a valid GUID. Known UUID ' .
1684
-				'({uuid}) probably does not match UUID configuration.',
1685
-				[ 'app' => 'user_ldap', 'uuid' => $guid ]
1686
-			);
1687
-			return $guid;
1688
-		}
1689
-		for($i=0; $i < 3; $i++) {
1690
-			$pairs = str_split($blocks[$i], 2);
1691
-			$pairs = array_reverse($pairs);
1692
-			$blocks[$i] = implode('', $pairs);
1693
-		}
1694
-		for($i=0; $i < 5; $i++) {
1695
-			$pairs = str_split($blocks[$i], 2);
1696
-			$blocks[$i] = '\\' . implode('\\', $pairs);
1697
-		}
1698
-		return implode('', $blocks);
1699
-	}
1700
-
1701
-	/**
1702
-	 * gets a SID of the domain of the given dn
1703
-	 * @param string $dn
1704
-	 * @return string|bool
1705
-	 */
1706
-	public function getSID($dn) {
1707
-		$domainDN = $this->getDomainDNFromDN($dn);
1708
-		$cacheKey = 'getSID-'.$domainDN;
1709
-		$sid = $this->connection->getFromCache($cacheKey);
1710
-		if(!is_null($sid)) {
1711
-			return $sid;
1712
-		}
1713
-
1714
-		$objectSid = $this->readAttribute($domainDN, 'objectsid');
1715
-		if(!is_array($objectSid) || empty($objectSid)) {
1716
-			$this->connection->writeToCache($cacheKey, false);
1717
-			return false;
1718
-		}
1719
-		$domainObjectSid = $this->convertSID2Str($objectSid[0]);
1720
-		$this->connection->writeToCache($cacheKey, $domainObjectSid);
1721
-
1722
-		return $domainObjectSid;
1723
-	}
1724
-
1725
-	/**
1726
-	 * converts a binary SID into a string representation
1727
-	 * @param string $sid
1728
-	 * @return string
1729
-	 */
1730
-	public function convertSID2Str($sid) {
1731
-		// The format of a SID binary string is as follows:
1732
-		// 1 byte for the revision level
1733
-		// 1 byte for the number n of variable sub-ids
1734
-		// 6 bytes for identifier authority value
1735
-		// n*4 bytes for n sub-ids
1736
-		//
1737
-		// Example: 010400000000000515000000a681e50e4d6c6c2bca32055f
1738
-		//  Legend: RRNNAAAAAAAAAAAA11111111222222223333333344444444
1739
-		$revision = ord($sid[0]);
1740
-		$numberSubID = ord($sid[1]);
1741
-
1742
-		$subIdStart = 8; // 1 + 1 + 6
1743
-		$subIdLength = 4;
1744
-		if (strlen($sid) !== $subIdStart + $subIdLength * $numberSubID) {
1745
-			// Incorrect number of bytes present.
1746
-			return '';
1747
-		}
1748
-
1749
-		// 6 bytes = 48 bits can be represented using floats without loss of
1750
-		// precision (see https://gist.github.com/bantu/886ac680b0aef5812f71)
1751
-		$iav = number_format(hexdec(bin2hex(substr($sid, 2, 6))), 0, '', '');
1752
-
1753
-		$subIDs = array();
1754
-		for ($i = 0; $i < $numberSubID; $i++) {
1755
-			$subID = unpack('V', substr($sid, $subIdStart + $subIdLength * $i, $subIdLength));
1756
-			$subIDs[] = sprintf('%u', $subID[1]);
1757
-		}
1758
-
1759
-		// Result for example above: S-1-5-21-249921958-728525901-1594176202
1760
-		return sprintf('S-%d-%s-%s', $revision, $iav, implode('-', $subIDs));
1761
-	}
1762
-
1763
-	/**
1764
-	 * checks if the given DN is part of the given base DN(s)
1765
-	 * @param string $dn the DN
1766
-	 * @param string[] $bases array containing the allowed base DN or DNs
1767
-	 * @return bool
1768
-	 */
1769
-	public function isDNPartOfBase($dn, $bases) {
1770
-		$belongsToBase = false;
1771
-		$bases = $this->helper->sanitizeDN($bases);
1772
-
1773
-		foreach($bases as $base) {
1774
-			$belongsToBase = true;
1775
-			if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base, 'UTF-8'))) {
1776
-				$belongsToBase = false;
1777
-			}
1778
-			if($belongsToBase) {
1779
-				break;
1780
-			}
1781
-		}
1782
-		return $belongsToBase;
1783
-	}
1784
-
1785
-	/**
1786
-	 * resets a running Paged Search operation
1787
-	 */
1788
-	private function abandonPagedSearch() {
1789
-		if($this->connection->hasPagedResultSupport) {
1790
-			$cr = $this->connection->getConnectionResource();
1791
-			$this->invokeLDAPMethod('controlPagedResult', $cr, 0, false, $this->lastCookie);
1792
-			$this->getPagedSearchResultState();
1793
-			$this->lastCookie = '';
1794
-			$this->cookies = array();
1795
-		}
1796
-	}
1797
-
1798
-	/**
1799
-	 * get a cookie for the next LDAP paged search
1800
-	 * @param string $base a string with the base DN for the search
1801
-	 * @param string $filter the search filter to identify the correct search
1802
-	 * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1803
-	 * @param int $offset the offset for the new search to identify the correct search really good
1804
-	 * @return string containing the key or empty if none is cached
1805
-	 */
1806
-	private function getPagedResultCookie($base, $filter, $limit, $offset) {
1807
-		if($offset === 0) {
1808
-			return '';
1809
-		}
1810
-		$offset -= $limit;
1811
-		//we work with cache here
1812
-		$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . intval($limit) . '-' . intval($offset);
1813
-		$cookie = '';
1814
-		if(isset($this->cookies[$cacheKey])) {
1815
-			$cookie = $this->cookies[$cacheKey];
1816
-			if(is_null($cookie)) {
1817
-				$cookie = '';
1818
-			}
1819
-		}
1820
-		return $cookie;
1821
-	}
1822
-
1823
-	/**
1824
-	 * checks whether an LDAP paged search operation has more pages that can be
1825
-	 * retrieved, typically when offset and limit are provided.
1826
-	 *
1827
-	 * Be very careful to use it: the last cookie value, which is inspected, can
1828
-	 * be reset by other operations. Best, call it immediately after a search(),
1829
-	 * searchUsers() or searchGroups() call. count-methods are probably safe as
1830
-	 * well. Don't rely on it with any fetchList-method.
1831
-	 * @return bool
1832
-	 */
1833
-	public function hasMoreResults() {
1834
-		if(!$this->connection->hasPagedResultSupport) {
1835
-			return false;
1836
-		}
1837
-
1838
-		if(empty($this->lastCookie) && $this->lastCookie !== '0') {
1839
-			// as in RFC 2696, when all results are returned, the cookie will
1840
-			// be empty.
1841
-			return false;
1842
-		}
1843
-
1844
-		return true;
1845
-	}
1846
-
1847
-	/**
1848
-	 * set a cookie for LDAP paged search run
1849
-	 * @param string $base a string with the base DN for the search
1850
-	 * @param string $filter the search filter to identify the correct search
1851
-	 * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1852
-	 * @param int $offset the offset for the run search to identify the correct search really good
1853
-	 * @param string $cookie string containing the cookie returned by ldap_control_paged_result_response
1854
-	 * @return void
1855
-	 */
1856
-	private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
1857
-		// allow '0' for 389ds
1858
-		if(!empty($cookie) || $cookie === '0') {
1859
-			$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .intval($limit) . '-' . intval($offset);
1860
-			$this->cookies[$cacheKey] = $cookie;
1861
-			$this->lastCookie = $cookie;
1862
-		}
1863
-	}
1864
-
1865
-	/**
1866
-	 * Check whether the most recent paged search was successful. It flushed the state var. Use it always after a possible paged search.
1867
-	 * @return boolean|null true on success, null or false otherwise
1868
-	 */
1869
-	public function getPagedSearchResultState() {
1870
-		$result = $this->pagedSearchedSuccessful;
1871
-		$this->pagedSearchedSuccessful = null;
1872
-		return $result;
1873
-	}
1874
-
1875
-	/**
1876
-	 * Prepares a paged search, if possible
1877
-	 * @param string $filter the LDAP filter for the search
1878
-	 * @param string[] $bases an array containing the LDAP subtree(s) that shall be searched
1879
-	 * @param string[] $attr optional, when a certain attribute shall be filtered outside
1880
-	 * @param int $limit
1881
-	 * @param int $offset
1882
-	 * @return bool|true
1883
-	 */
1884
-	private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
1885
-		$pagedSearchOK = false;
1886
-		if($this->connection->hasPagedResultSupport && ($limit !== 0)) {
1887
-			$offset = intval($offset); //can be null
1888
-			\OCP\Util::writeLog('user_ldap',
1889
-				'initializing paged search for  Filter '.$filter.' base '.print_r($bases, true)
1890
-				.' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset,
1891
-				\OCP\Util::DEBUG);
1892
-			//get the cookie from the search for the previous search, required by LDAP
1893
-			foreach($bases as $base) {
1894
-
1895
-				$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1896
-				if(empty($cookie) && $cookie !== "0" && ($offset > 0)) {
1897
-					// no cookie known from a potential previous search. We need
1898
-					// to start from 0 to come to the desired page. cookie value
1899
-					// of '0' is valid, because 389ds
1900
-					$reOffset = 0;
1901
-					while($reOffset < $offset) {
1902
-						$this->search($filter, array($base), $attr, $limit, $reOffset, true);
1903
-						$reOffset += $limit;
1904
-					}
1905
-					$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1906
-					//still no cookie? obviously, the server does not like us. Let's skip paging efforts.
1907
-					// '0' is valid, because 389ds
1908
-					//TODO: remember this, probably does not change in the next request...
1909
-					if(empty($cookie) && $cookie !== '0') {
1910
-						$cookie = null;
1911
-					}
1912
-				}
1913
-				if(!is_null($cookie)) {
1914
-					//since offset = 0, this is a new search. We abandon other searches that might be ongoing.
1915
-					$this->abandonPagedSearch();
1916
-					$pagedSearchOK = $this->invokeLDAPMethod('controlPagedResult',
1917
-						$this->connection->getConnectionResource(), $limit,
1918
-						false, $cookie);
1919
-					if(!$pagedSearchOK) {
1920
-						return false;
1921
-					}
1922
-					\OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::DEBUG);
1923
-				} else {
1924
-					\OCP\Util::writeLog('user_ldap',
1925
-						'No paged search for us, Cpt., Limit '.$limit.' Offset '.$offset,
1926
-						\OCP\Util::INFO);
1927
-				}
1928
-
1929
-			}
1930
-		/* ++ Fixing RHDS searches with pages with zero results ++
1682
+            \OC::$server->getLogger()->info(
1683
+                'Passed string does not resemble a valid GUID. Known UUID ' .
1684
+                '({uuid}) probably does not match UUID configuration.',
1685
+                [ 'app' => 'user_ldap', 'uuid' => $guid ]
1686
+            );
1687
+            return $guid;
1688
+        }
1689
+        for($i=0; $i < 3; $i++) {
1690
+            $pairs = str_split($blocks[$i], 2);
1691
+            $pairs = array_reverse($pairs);
1692
+            $blocks[$i] = implode('', $pairs);
1693
+        }
1694
+        for($i=0; $i < 5; $i++) {
1695
+            $pairs = str_split($blocks[$i], 2);
1696
+            $blocks[$i] = '\\' . implode('\\', $pairs);
1697
+        }
1698
+        return implode('', $blocks);
1699
+    }
1700
+
1701
+    /**
1702
+     * gets a SID of the domain of the given dn
1703
+     * @param string $dn
1704
+     * @return string|bool
1705
+     */
1706
+    public function getSID($dn) {
1707
+        $domainDN = $this->getDomainDNFromDN($dn);
1708
+        $cacheKey = 'getSID-'.$domainDN;
1709
+        $sid = $this->connection->getFromCache($cacheKey);
1710
+        if(!is_null($sid)) {
1711
+            return $sid;
1712
+        }
1713
+
1714
+        $objectSid = $this->readAttribute($domainDN, 'objectsid');
1715
+        if(!is_array($objectSid) || empty($objectSid)) {
1716
+            $this->connection->writeToCache($cacheKey, false);
1717
+            return false;
1718
+        }
1719
+        $domainObjectSid = $this->convertSID2Str($objectSid[0]);
1720
+        $this->connection->writeToCache($cacheKey, $domainObjectSid);
1721
+
1722
+        return $domainObjectSid;
1723
+    }
1724
+
1725
+    /**
1726
+     * converts a binary SID into a string representation
1727
+     * @param string $sid
1728
+     * @return string
1729
+     */
1730
+    public function convertSID2Str($sid) {
1731
+        // The format of a SID binary string is as follows:
1732
+        // 1 byte for the revision level
1733
+        // 1 byte for the number n of variable sub-ids
1734
+        // 6 bytes for identifier authority value
1735
+        // n*4 bytes for n sub-ids
1736
+        //
1737
+        // Example: 010400000000000515000000a681e50e4d6c6c2bca32055f
1738
+        //  Legend: RRNNAAAAAAAAAAAA11111111222222223333333344444444
1739
+        $revision = ord($sid[0]);
1740
+        $numberSubID = ord($sid[1]);
1741
+
1742
+        $subIdStart = 8; // 1 + 1 + 6
1743
+        $subIdLength = 4;
1744
+        if (strlen($sid) !== $subIdStart + $subIdLength * $numberSubID) {
1745
+            // Incorrect number of bytes present.
1746
+            return '';
1747
+        }
1748
+
1749
+        // 6 bytes = 48 bits can be represented using floats without loss of
1750
+        // precision (see https://gist.github.com/bantu/886ac680b0aef5812f71)
1751
+        $iav = number_format(hexdec(bin2hex(substr($sid, 2, 6))), 0, '', '');
1752
+
1753
+        $subIDs = array();
1754
+        for ($i = 0; $i < $numberSubID; $i++) {
1755
+            $subID = unpack('V', substr($sid, $subIdStart + $subIdLength * $i, $subIdLength));
1756
+            $subIDs[] = sprintf('%u', $subID[1]);
1757
+        }
1758
+
1759
+        // Result for example above: S-1-5-21-249921958-728525901-1594176202
1760
+        return sprintf('S-%d-%s-%s', $revision, $iav, implode('-', $subIDs));
1761
+    }
1762
+
1763
+    /**
1764
+     * checks if the given DN is part of the given base DN(s)
1765
+     * @param string $dn the DN
1766
+     * @param string[] $bases array containing the allowed base DN or DNs
1767
+     * @return bool
1768
+     */
1769
+    public function isDNPartOfBase($dn, $bases) {
1770
+        $belongsToBase = false;
1771
+        $bases = $this->helper->sanitizeDN($bases);
1772
+
1773
+        foreach($bases as $base) {
1774
+            $belongsToBase = true;
1775
+            if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base, 'UTF-8'))) {
1776
+                $belongsToBase = false;
1777
+            }
1778
+            if($belongsToBase) {
1779
+                break;
1780
+            }
1781
+        }
1782
+        return $belongsToBase;
1783
+    }
1784
+
1785
+    /**
1786
+     * resets a running Paged Search operation
1787
+     */
1788
+    private function abandonPagedSearch() {
1789
+        if($this->connection->hasPagedResultSupport) {
1790
+            $cr = $this->connection->getConnectionResource();
1791
+            $this->invokeLDAPMethod('controlPagedResult', $cr, 0, false, $this->lastCookie);
1792
+            $this->getPagedSearchResultState();
1793
+            $this->lastCookie = '';
1794
+            $this->cookies = array();
1795
+        }
1796
+    }
1797
+
1798
+    /**
1799
+     * get a cookie for the next LDAP paged search
1800
+     * @param string $base a string with the base DN for the search
1801
+     * @param string $filter the search filter to identify the correct search
1802
+     * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1803
+     * @param int $offset the offset for the new search to identify the correct search really good
1804
+     * @return string containing the key or empty if none is cached
1805
+     */
1806
+    private function getPagedResultCookie($base, $filter, $limit, $offset) {
1807
+        if($offset === 0) {
1808
+            return '';
1809
+        }
1810
+        $offset -= $limit;
1811
+        //we work with cache here
1812
+        $cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . intval($limit) . '-' . intval($offset);
1813
+        $cookie = '';
1814
+        if(isset($this->cookies[$cacheKey])) {
1815
+            $cookie = $this->cookies[$cacheKey];
1816
+            if(is_null($cookie)) {
1817
+                $cookie = '';
1818
+            }
1819
+        }
1820
+        return $cookie;
1821
+    }
1822
+
1823
+    /**
1824
+     * checks whether an LDAP paged search operation has more pages that can be
1825
+     * retrieved, typically when offset and limit are provided.
1826
+     *
1827
+     * Be very careful to use it: the last cookie value, which is inspected, can
1828
+     * be reset by other operations. Best, call it immediately after a search(),
1829
+     * searchUsers() or searchGroups() call. count-methods are probably safe as
1830
+     * well. Don't rely on it with any fetchList-method.
1831
+     * @return bool
1832
+     */
1833
+    public function hasMoreResults() {
1834
+        if(!$this->connection->hasPagedResultSupport) {
1835
+            return false;
1836
+        }
1837
+
1838
+        if(empty($this->lastCookie) && $this->lastCookie !== '0') {
1839
+            // as in RFC 2696, when all results are returned, the cookie will
1840
+            // be empty.
1841
+            return false;
1842
+        }
1843
+
1844
+        return true;
1845
+    }
1846
+
1847
+    /**
1848
+     * set a cookie for LDAP paged search run
1849
+     * @param string $base a string with the base DN for the search
1850
+     * @param string $filter the search filter to identify the correct search
1851
+     * @param int $limit the limit (or 'pageSize'), to identify the correct search well
1852
+     * @param int $offset the offset for the run search to identify the correct search really good
1853
+     * @param string $cookie string containing the cookie returned by ldap_control_paged_result_response
1854
+     * @return void
1855
+     */
1856
+    private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
1857
+        // allow '0' for 389ds
1858
+        if(!empty($cookie) || $cookie === '0') {
1859
+            $cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .intval($limit) . '-' . intval($offset);
1860
+            $this->cookies[$cacheKey] = $cookie;
1861
+            $this->lastCookie = $cookie;
1862
+        }
1863
+    }
1864
+
1865
+    /**
1866
+     * Check whether the most recent paged search was successful. It flushed the state var. Use it always after a possible paged search.
1867
+     * @return boolean|null true on success, null or false otherwise
1868
+     */
1869
+    public function getPagedSearchResultState() {
1870
+        $result = $this->pagedSearchedSuccessful;
1871
+        $this->pagedSearchedSuccessful = null;
1872
+        return $result;
1873
+    }
1874
+
1875
+    /**
1876
+     * Prepares a paged search, if possible
1877
+     * @param string $filter the LDAP filter for the search
1878
+     * @param string[] $bases an array containing the LDAP subtree(s) that shall be searched
1879
+     * @param string[] $attr optional, when a certain attribute shall be filtered outside
1880
+     * @param int $limit
1881
+     * @param int $offset
1882
+     * @return bool|true
1883
+     */
1884
+    private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
1885
+        $pagedSearchOK = false;
1886
+        if($this->connection->hasPagedResultSupport && ($limit !== 0)) {
1887
+            $offset = intval($offset); //can be null
1888
+            \OCP\Util::writeLog('user_ldap',
1889
+                'initializing paged search for  Filter '.$filter.' base '.print_r($bases, true)
1890
+                .' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset,
1891
+                \OCP\Util::DEBUG);
1892
+            //get the cookie from the search for the previous search, required by LDAP
1893
+            foreach($bases as $base) {
1894
+
1895
+                $cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1896
+                if(empty($cookie) && $cookie !== "0" && ($offset > 0)) {
1897
+                    // no cookie known from a potential previous search. We need
1898
+                    // to start from 0 to come to the desired page. cookie value
1899
+                    // of '0' is valid, because 389ds
1900
+                    $reOffset = 0;
1901
+                    while($reOffset < $offset) {
1902
+                        $this->search($filter, array($base), $attr, $limit, $reOffset, true);
1903
+                        $reOffset += $limit;
1904
+                    }
1905
+                    $cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1906
+                    //still no cookie? obviously, the server does not like us. Let's skip paging efforts.
1907
+                    // '0' is valid, because 389ds
1908
+                    //TODO: remember this, probably does not change in the next request...
1909
+                    if(empty($cookie) && $cookie !== '0') {
1910
+                        $cookie = null;
1911
+                    }
1912
+                }
1913
+                if(!is_null($cookie)) {
1914
+                    //since offset = 0, this is a new search. We abandon other searches that might be ongoing.
1915
+                    $this->abandonPagedSearch();
1916
+                    $pagedSearchOK = $this->invokeLDAPMethod('controlPagedResult',
1917
+                        $this->connection->getConnectionResource(), $limit,
1918
+                        false, $cookie);
1919
+                    if(!$pagedSearchOK) {
1920
+                        return false;
1921
+                    }
1922
+                    \OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::DEBUG);
1923
+                } else {
1924
+                    \OCP\Util::writeLog('user_ldap',
1925
+                        'No paged search for us, Cpt., Limit '.$limit.' Offset '.$offset,
1926
+                        \OCP\Util::INFO);
1927
+                }
1928
+
1929
+            }
1930
+        /* ++ Fixing RHDS searches with pages with zero results ++
1931 1931
 		 * We coudn't get paged searches working with our RHDS for login ($limit = 0),
1932 1932
 		 * due to pages with zero results.
1933 1933
 		 * So we added "&& !empty($this->lastCookie)" to this test to ignore pagination
1934 1934
 		 * if we don't have a previous paged search.
1935 1935
 		 */
1936
-		} else if($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) {
1937
-			// a search without limit was requested. However, if we do use
1938
-			// Paged Search once, we always must do it. This requires us to
1939
-			// initialize it with the configured page size.
1940
-			$this->abandonPagedSearch();
1941
-			// in case someone set it to 0 … use 500, otherwise no results will
1942
-			// be returned.
1943
-			$pageSize = intval($this->connection->ldapPagingSize) > 0 ? intval($this->connection->ldapPagingSize) : 500;
1944
-			$pagedSearchOK = $this->invokeLDAPMethod('controlPagedResult',
1945
-				$this->connection->getConnectionResource(),
1946
-				$pageSize, false, '');
1947
-		}
1948
-
1949
-		return $pagedSearchOK;
1950
-	}
1936
+        } else if($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) {
1937
+            // a search without limit was requested. However, if we do use
1938
+            // Paged Search once, we always must do it. This requires us to
1939
+            // initialize it with the configured page size.
1940
+            $this->abandonPagedSearch();
1941
+            // in case someone set it to 0 … use 500, otherwise no results will
1942
+            // be returned.
1943
+            $pageSize = intval($this->connection->ldapPagingSize) > 0 ? intval($this->connection->ldapPagingSize) : 500;
1944
+            $pagedSearchOK = $this->invokeLDAPMethod('controlPagedResult',
1945
+                $this->connection->getConnectionResource(),
1946
+                $pageSize, false, '');
1947
+        }
1948
+
1949
+        return $pagedSearchOK;
1950
+    }
1951 1951
 
1952 1952
 }
Please login to merge, or discard this patch.
Spacing   +168 added lines, -168 removed lines patch added patch discarded remove patch
@@ -121,7 +121,7 @@  discard block
 block discarded – undo
121 121
 	 * @return AbstractMapping
122 122
 	 */
123 123
 	public function getUserMapper() {
124
-		if(is_null($this->userMapper)) {
124
+		if (is_null($this->userMapper)) {
125 125
 			throw new \Exception('UserMapper was not assigned to this Access instance.');
126 126
 		}
127 127
 		return $this->userMapper;
@@ -141,7 +141,7 @@  discard block
 block discarded – undo
141 141
 	 * @return AbstractMapping
142 142
 	 */
143 143
 	public function getGroupMapper() {
144
-		if(is_null($this->groupMapper)) {
144
+		if (is_null($this->groupMapper)) {
145 145
 			throw new \Exception('GroupMapper was not assigned to this Access instance.');
146 146
 		}
147 147
 		return $this->groupMapper;
@@ -172,14 +172,14 @@  discard block
 block discarded – undo
172 172
 	 *          array if $attr is empty, false otherwise
173 173
 	 */
174 174
 	public function readAttribute($dn, $attr, $filter = 'objectClass=*') {
175
-		if(!$this->checkConnection()) {
175
+		if (!$this->checkConnection()) {
176 176
 			\OCP\Util::writeLog('user_ldap',
177 177
 				'No LDAP Connector assigned, access impossible for readAttribute.',
178 178
 				\OCP\Util::WARN);
179 179
 			return false;
180 180
 		}
181 181
 		$cr = $this->connection->getConnectionResource();
182
-		if(!$this->ldap->isResource($cr)) {
182
+		if (!$this->ldap->isResource($cr)) {
183 183
 			//LDAP not available
184 184
 			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
185 185
 			return false;
@@ -202,7 +202,7 @@  discard block
 block discarded – undo
202 202
 		$isRangeRequest = false;
203 203
 		do {
204 204
 			$result = $this->executeRead($cr, $dn, $attrToRead, $filter, $maxResults);
205
-			if(is_bool($result)) {
205
+			if (is_bool($result)) {
206 206
 				// when an exists request was run and it was successful, an empty
207 207
 				// array must be returned
208 208
 				return $result ? [] : false;
@@ -219,22 +219,22 @@  discard block
 block discarded – undo
219 219
 			$result = $this->extractRangeData($result, $attr);
220 220
 			if (!empty($result)) {
221 221
 				$normalizedResult = $this->extractAttributeValuesFromResult(
222
-					[ $attr => $result['values'] ],
222
+					[$attr => $result['values']],
223 223
 					$attr
224 224
 				);
225 225
 				$values = array_merge($values, $normalizedResult);
226 226
 
227
-				if($result['rangeHigh'] === '*') {
227
+				if ($result['rangeHigh'] === '*') {
228 228
 					// when server replies with * as high range value, there are
229 229
 					// no more results left
230 230
 					return $values;
231 231
 				} else {
232
-					$low  = $result['rangeHigh'] + 1;
233
-					$attrToRead = $result['attributeName'] . ';range=' . $low . '-*';
232
+					$low = $result['rangeHigh'] + 1;
233
+					$attrToRead = $result['attributeName'].';range='.$low.'-*';
234 234
 					$isRangeRequest = true;
235 235
 				}
236 236
 			}
237
-		} while($isRangeRequest);
237
+		} while ($isRangeRequest);
238 238
 
239 239
 		\OCP\Util::writeLog('user_ldap', 'Requested attribute '.$attr.' not found for '.$dn, \OCP\Util::DEBUG);
240 240
 		return false;
@@ -259,13 +259,13 @@  discard block
 block discarded – undo
259 259
 		if (!$this->ldap->isResource($rr)) {
260 260
 			if ($attribute !== '') {
261 261
 				//do not throw this message on userExists check, irritates
262
-				\OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN ' . $dn, \OCP\Util::DEBUG);
262
+				\OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, \OCP\Util::DEBUG);
263 263
 			}
264 264
 			//in case an error occurs , e.g. object does not exist
265 265
 			return false;
266 266
 		}
267 267
 		if ($attribute === '' && ($filter === 'objectclass=*' || $this->invokeLDAPMethod('countEntries', $cr, $rr) === 1)) {
268
-			\OCP\Util::writeLog('user_ldap', 'readAttribute: ' . $dn . ' found', \OCP\Util::DEBUG);
268
+			\OCP\Util::writeLog('user_ldap', 'readAttribute: '.$dn.' found', \OCP\Util::DEBUG);
269 269
 			return true;
270 270
 		}
271 271
 		$er = $this->invokeLDAPMethod('firstEntry', $cr, $rr);
@@ -290,12 +290,12 @@  discard block
 block discarded – undo
290 290
 	 */
291 291
 	public function extractAttributeValuesFromResult($result, $attribute) {
292 292
 		$values = [];
293
-		if(isset($result[$attribute]) && $result[$attribute]['count'] > 0) {
293
+		if (isset($result[$attribute]) && $result[$attribute]['count'] > 0) {
294 294
 			$lowercaseAttribute = strtolower($attribute);
295
-			for($i=0;$i<$result[$attribute]['count'];$i++) {
296
-				if($this->resemblesDN($attribute)) {
295
+			for ($i = 0; $i < $result[$attribute]['count']; $i++) {
296
+				if ($this->resemblesDN($attribute)) {
297 297
 					$values[] = $this->helper->sanitizeDN($result[$attribute][$i]);
298
-				} elseif($lowercaseAttribute === 'objectguid' || $lowercaseAttribute === 'guid') {
298
+				} elseif ($lowercaseAttribute === 'objectguid' || $lowercaseAttribute === 'guid') {
299 299
 					$values[] = $this->convertObjectGUID2Str($result[$attribute][$i]);
300 300
 				} else {
301 301
 					$values[] = $result[$attribute][$i];
@@ -317,10 +317,10 @@  discard block
 block discarded – undo
317 317
 	 */
318 318
 	public function extractRangeData($result, $attribute) {
319 319
 		$keys = array_keys($result);
320
-		foreach($keys as $key) {
321
-			if($key !== $attribute && strpos($key, $attribute) === 0) {
320
+		foreach ($keys as $key) {
321
+			if ($key !== $attribute && strpos($key, $attribute) === 0) {
322 322
 				$queryData = explode(';', $key);
323
-				if(strpos($queryData[1], 'range=') === 0) {
323
+				if (strpos($queryData[1], 'range=') === 0) {
324 324
 					$high = substr($queryData[1], 1 + strpos($queryData[1], '-'));
325 325
 					$data = [
326 326
 						'values' => $result[$key],
@@ -345,18 +345,18 @@  discard block
 block discarded – undo
345 345
 	 * @throws \Exception
346 346
 	 */
347 347
 	public function setPassword($userDN, $password) {
348
-		if(intval($this->connection->turnOnPasswordChange) !== 1) {
348
+		if (intval($this->connection->turnOnPasswordChange) !== 1) {
349 349
 			throw new \Exception('LDAP password changes are disabled.');
350 350
 		}
351 351
 		$cr = $this->connection->getConnectionResource();
352
-		if(!$this->ldap->isResource($cr)) {
352
+		if (!$this->ldap->isResource($cr)) {
353 353
 			//LDAP not available
354 354
 			\OCP\Util::writeLog('user_ldap', 'LDAP resource not available.', \OCP\Util::DEBUG);
355 355
 			return false;
356 356
 		}
357 357
 		try {
358 358
 			return @$this->invokeLDAPMethod('modReplace', $cr, $userDN, $password);
359
-		} catch(ConstraintViolationException $e) {
359
+		} catch (ConstraintViolationException $e) {
360 360
 			throw new HintException('Password change rejected.', \OC::$server->getL10N('user_ldap')->t('Password change rejected. Hint: ').$e->getMessage(), $e->getCode());
361 361
 		}
362 362
 	}
@@ -398,17 +398,17 @@  discard block
 block discarded – undo
398 398
 	 */
399 399
 	public function getDomainDNFromDN($dn) {
400 400
 		$allParts = $this->ldap->explodeDN($dn, 0);
401
-		if($allParts === false) {
401
+		if ($allParts === false) {
402 402
 			//not a valid DN
403 403
 			return '';
404 404
 		}
405 405
 		$domainParts = array();
406 406
 		$dcFound = false;
407
-		foreach($allParts as $part) {
408
-			if(!$dcFound && strpos($part, 'dc=') === 0) {
407
+		foreach ($allParts as $part) {
408
+			if (!$dcFound && strpos($part, 'dc=') === 0) {
409 409
 				$dcFound = true;
410 410
 			}
411
-			if($dcFound) {
411
+			if ($dcFound) {
412 412
 				$domainParts[] = $part;
413 413
 			}
414 414
 		}
@@ -435,7 +435,7 @@  discard block
 block discarded – undo
435 435
 
436 436
 		//Check whether the DN belongs to the Base, to avoid issues on multi-
437 437
 		//server setups
438
-		if(is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
438
+		if (is_string($fdn) && $this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
439 439
 			return $fdn;
440 440
 		}
441 441
 
@@ -452,7 +452,7 @@  discard block
 block discarded – undo
452 452
 		//To avoid bypassing the base DN settings under certain circumstances
453 453
 		//with the group support, check whether the provided DN matches one of
454 454
 		//the given Bases
455
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
455
+		if (!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseGroups)) {
456 456
 			return false;
457 457
 		}
458 458
 
@@ -469,11 +469,11 @@  discard block
 block discarded – undo
469 469
 	 */
470 470
 	public function groupsMatchFilter($groupDNs) {
471 471
 		$validGroupDNs = [];
472
-		foreach($groupDNs as $dn) {
472
+		foreach ($groupDNs as $dn) {
473 473
 			$cacheKey = 'groupsMatchFilter-'.$dn;
474 474
 			$groupMatchFilter = $this->connection->getFromCache($cacheKey);
475
-			if(!is_null($groupMatchFilter)) {
476
-				if($groupMatchFilter) {
475
+			if (!is_null($groupMatchFilter)) {
476
+				if ($groupMatchFilter) {
477 477
 					$validGroupDNs[] = $dn;
478 478
 				}
479 479
 				continue;
@@ -481,13 +481,13 @@  discard block
 block discarded – undo
481 481
 
482 482
 			// Check the base DN first. If this is not met already, we don't
483 483
 			// need to ask the server at all.
484
-			if(!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
484
+			if (!$this->isDNPartOfBase($dn, $this->connection->ldapBaseGroups)) {
485 485
 				$this->connection->writeToCache($cacheKey, false);
486 486
 				continue;
487 487
 			}
488 488
 
489 489
 			$result = $this->readAttribute($dn, 'cn', $this->connection->ldapGroupFilter);
490
-			if(is_array($result)) {
490
+			if (is_array($result)) {
491 491
 				$this->connection->writeToCache($cacheKey, true);
492 492
 				$validGroupDNs[] = $dn;
493 493
 			} else {
@@ -508,7 +508,7 @@  discard block
 block discarded – undo
508 508
 		//To avoid bypassing the base DN settings under certain circumstances
509 509
 		//with the group support, check whether the provided DN matches one of
510 510
 		//the given Bases
511
-		if(!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
511
+		if (!$this->isDNPartOfBase($fdn, $this->connection->ldapBaseUsers)) {
512 512
 			return false;
513 513
 		}
514 514
 
@@ -527,7 +527,7 @@  discard block
 block discarded – undo
527 527
 	 */
528 528
 	public function dn2ocname($fdn, $ldapName = null, $isUser = true, &$newlyMapped = null, array $record = null) {
529 529
 		$newlyMapped = false;
530
-		if($isUser) {
530
+		if ($isUser) {
531 531
 			$mapper = $this->getUserMapper();
532 532
 			$nameAttribute = $this->connection->ldapUserDisplayName;
533 533
 		} else {
@@ -537,15 +537,15 @@  discard block
 block discarded – undo
537 537
 
538 538
 		//let's try to retrieve the Nextcloud name from the mappings table
539 539
 		$ncName = $mapper->getNameByDN($fdn);
540
-		if(is_string($ncName)) {
540
+		if (is_string($ncName)) {
541 541
 			return $ncName;
542 542
 		}
543 543
 
544 544
 		//second try: get the UUID and check if it is known. Then, update the DN and return the name.
545 545
 		$uuid = $this->getUUID($fdn, $isUser, $record);
546
-		if(is_string($uuid)) {
546
+		if (is_string($uuid)) {
547 547
 			$ncName = $mapper->getNameByUUID($uuid);
548
-			if(is_string($ncName)) {
548
+			if (is_string($ncName)) {
549 549
 				$mapper->setDNbyUUID($fdn, $uuid);
550 550
 				return $ncName;
551 551
 			}
@@ -555,16 +555,16 @@  discard block
 block discarded – undo
555 555
 			return false;
556 556
 		}
557 557
 
558
-		if(is_null($ldapName)) {
558
+		if (is_null($ldapName)) {
559 559
 			$ldapName = $this->readAttribute($fdn, $nameAttribute);
560
-			if(!isset($ldapName[0]) && empty($ldapName[0])) {
560
+			if (!isset($ldapName[0]) && empty($ldapName[0])) {
561 561
 				\OCP\Util::writeLog('user_ldap', 'No or empty name for '.$fdn.'.', \OCP\Util::INFO);
562 562
 				return false;
563 563
 			}
564 564
 			$ldapName = $ldapName[0];
565 565
 		}
566 566
 
567
-		if($isUser) {
567
+		if ($isUser) {
568 568
 			$usernameAttribute = strval($this->connection->ldapExpertUsernameAttr);
569 569
 			if ($usernameAttribute !== '') {
570 570
 				$username = $this->readAttribute($fdn, $usernameAttribute);
@@ -583,9 +583,9 @@  discard block
 block discarded – undo
583 583
 		// outside of core user management will still cache the user as non-existing.
584 584
 		$originalTTL = $this->connection->ldapCacheTTL;
585 585
 		$this->connection->setConfiguration(array('ldapCacheTTL' => 0));
586
-		if(($isUser && !\OCP\User::userExists($intName))
586
+		if (($isUser && !\OCP\User::userExists($intName))
587 587
 			|| (!$isUser && !\OC::$server->getGroupManager()->groupExists($intName))) {
588
-			if($mapper->map($fdn, $intName, $uuid)) {
588
+			if ($mapper->map($fdn, $intName, $uuid)) {
589 589
 				$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
590 590
 				$newlyMapped = true;
591 591
 				return $intName;
@@ -594,7 +594,7 @@  discard block
 block discarded – undo
594 594
 		$this->connection->setConfiguration(array('ldapCacheTTL' => $originalTTL));
595 595
 
596 596
 		$altName = $this->createAltInternalOwnCloudName($intName, $isUser);
597
-		if(is_string($altName) && $mapper->map($fdn, $altName, $uuid)) {
597
+		if (is_string($altName) && $mapper->map($fdn, $altName, $uuid)) {
598 598
 			$newlyMapped = true;
599 599
 			return $altName;
600 600
 		}
@@ -632,7 +632,7 @@  discard block
 block discarded – undo
632 632
 	 * @return array
633 633
 	 */
634 634
 	private function ldap2NextcloudNames($ldapObjects, $isUsers) {
635
-		if($isUsers) {
635
+		if ($isUsers) {
636 636
 			$nameAttribute = $this->connection->ldapUserDisplayName;
637 637
 			$sndAttribute  = $this->connection->ldapUserDisplayName2;
638 638
 		} else {
@@ -640,9 +640,9 @@  discard block
 block discarded – undo
640 640
 		}
641 641
 		$nextcloudNames = array();
642 642
 
643
-		foreach($ldapObjects as $ldapObject) {
643
+		foreach ($ldapObjects as $ldapObject) {
644 644
 			$nameByLDAP = null;
645
-			if(    isset($ldapObject[$nameAttribute])
645
+			if (isset($ldapObject[$nameAttribute])
646 646
 				&& is_array($ldapObject[$nameAttribute])
647 647
 				&& isset($ldapObject[$nameAttribute][0])
648 648
 			) {
@@ -651,12 +651,12 @@  discard block
 block discarded – undo
651 651
 			}
652 652
 
653 653
 			$ncName = $this->dn2ocname($ldapObject['dn'][0], $nameByLDAP, $isUsers);
654
-			if($ncName) {
654
+			if ($ncName) {
655 655
 				$nextcloudNames[] = $ncName;
656
-				if($isUsers) {
656
+				if ($isUsers) {
657 657
 					//cache the user names so it does not need to be retrieved
658 658
 					//again later (e.g. sharing dialogue).
659
-					if(is_null($nameByLDAP)) {
659
+					if (is_null($nameByLDAP)) {
660 660
 						continue;
661 661
 					}
662 662
 					$sndName = isset($ldapObject[$sndAttribute][0])
@@ -694,7 +694,7 @@  discard block
 block discarded – undo
694 694
 	 */
695 695
 	public function cacheUserDisplayName($ocName, $displayName, $displayName2 = '') {
696 696
 		$user = $this->userManager->get($ocName);
697
-		if($user === null) {
697
+		if ($user === null) {
698 698
 			return;
699 699
 		}
700 700
 		$displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
@@ -714,9 +714,9 @@  discard block
 block discarded – undo
714 714
 		$attempts = 0;
715 715
 		//while loop is just a precaution. If a name is not generated within
716 716
 		//20 attempts, something else is very wrong. Avoids infinite loop.
717
-		while($attempts < 20){
718
-			$altName = $name . '_' . rand(1000,9999);
719
-			if(!\OCP\User::userExists($altName)) {
717
+		while ($attempts < 20) {
718
+			$altName = $name.'_'.rand(1000, 9999);
719
+			if (!\OCP\User::userExists($altName)) {
720 720
 				return $altName;
721 721
 			}
722 722
 			$attempts++;
@@ -738,25 +738,25 @@  discard block
 block discarded – undo
738 738
 	 */
739 739
 	private function _createAltInternalOwnCloudNameForGroups($name) {
740 740
 		$usedNames = $this->groupMapper->getNamesBySearch($name, "", '_%');
741
-		if(!($usedNames) || count($usedNames) === 0) {
741
+		if (!($usedNames) || count($usedNames) === 0) {
742 742
 			$lastNo = 1; //will become name_2
743 743
 		} else {
744 744
 			natsort($usedNames);
745 745
 			$lastName = array_pop($usedNames);
746 746
 			$lastNo = intval(substr($lastName, strrpos($lastName, '_') + 1));
747 747
 		}
748
-		$altName = $name.'_'.strval($lastNo+1);
748
+		$altName = $name.'_'.strval($lastNo + 1);
749 749
 		unset($usedNames);
750 750
 
751 751
 		$attempts = 1;
752
-		while($attempts < 21){
752
+		while ($attempts < 21) {
753 753
 			// Check to be really sure it is unique
754 754
 			// while loop is just a precaution. If a name is not generated within
755 755
 			// 20 attempts, something else is very wrong. Avoids infinite loop.
756
-			if(!\OC::$server->getGroupManager()->groupExists($altName)) {
756
+			if (!\OC::$server->getGroupManager()->groupExists($altName)) {
757 757
 				return $altName;
758 758
 			}
759
-			$altName = $name . '_' . ($lastNo + $attempts);
759
+			$altName = $name.'_'.($lastNo + $attempts);
760 760
 			$attempts++;
761 761
 		}
762 762
 		return false;
@@ -771,7 +771,7 @@  discard block
 block discarded – undo
771 771
 	private function createAltInternalOwnCloudName($name, $isUser) {
772 772
 		$originalTTL = $this->connection->ldapCacheTTL;
773 773
 		$this->connection->setConfiguration(array('ldapCacheTTL' => 0));
774
-		if($isUser) {
774
+		if ($isUser) {
775 775
 			$altName = $this->_createAltInternalOwnCloudNameForUsers($name);
776 776
 		} else {
777 777
 			$altName = $this->_createAltInternalOwnCloudNameForGroups($name);
@@ -821,7 +821,7 @@  discard block
 block discarded – undo
821 821
 	public function fetchListOfUsers($filter, $attr, $limit = null, $offset = null, $forceApplyAttributes = false) {
822 822
 		$ldapRecords = $this->searchUsers($filter, $attr, $limit, $offset);
823 823
 		$recordsToUpdate = $ldapRecords;
824
-		if(!$forceApplyAttributes) {
824
+		if (!$forceApplyAttributes) {
825 825
 			$isBackgroundJobModeAjax = $this->c->getConfig()
826 826
 					->getAppValue('core', 'backgroundjobs_mode', 'ajax') === 'ajax';
827 827
 			$recordsToUpdate = array_filter($ldapRecords, function($record) use ($isBackgroundJobModeAjax) {
@@ -840,20 +840,20 @@  discard block
 block discarded – undo
840 840
 	 * and their values
841 841
 	 * @param array $ldapRecords
842 842
 	 */
843
-	public function batchApplyUserAttributes(array $ldapRecords){
843
+	public function batchApplyUserAttributes(array $ldapRecords) {
844 844
 		$displayNameAttribute = strtolower($this->connection->ldapUserDisplayName);
845
-		foreach($ldapRecords as $userRecord) {
846
-			if(!isset($userRecord[$displayNameAttribute])) {
845
+		foreach ($ldapRecords as $userRecord) {
846
+			if (!isset($userRecord[$displayNameAttribute])) {
847 847
 				// displayName is obligatory
848 848
 				continue;
849 849
 			}
850
-			$ocName  = $this->dn2ocname($userRecord['dn'][0], null, true);
851
-			if($ocName === false) {
850
+			$ocName = $this->dn2ocname($userRecord['dn'][0], null, true);
851
+			if ($ocName === false) {
852 852
 				continue;
853 853
 			}
854 854
 			$this->cacheUserExists($ocName);
855 855
 			$user = $this->userManager->get($ocName);
856
-			if($user instanceof OfflineUser) {
856
+			if ($user instanceof OfflineUser) {
857 857
 				$user->unmark();
858 858
 				$user = $this->userManager->get($ocName);
859 859
 			}
@@ -885,8 +885,8 @@  discard block
 block discarded – undo
885 885
 	 * @return array
886 886
 	 */
887 887
 	private function fetchList($list, $manyAttributes) {
888
-		if(is_array($list)) {
889
-			if($manyAttributes) {
888
+		if (is_array($list)) {
889
+			if ($manyAttributes) {
890 890
 				return $list;
891 891
 			} else {
892 892
 				$list = array_reduce($list, function($carry, $item) {
@@ -984,7 +984,7 @@  discard block
 block discarded – undo
984 984
 		// php no longer supports call-time pass-by-reference
985 985
 		// thus cannot support controlPagedResultResponse as the third argument
986 986
 		// is a reference
987
-		$doMethod = function () use ($command, &$arguments) {
987
+		$doMethod = function() use ($command, &$arguments) {
988 988
 			if ($command == 'controlPagedResultResponse') {
989 989
 				throw new \InvalidArgumentException('Invoker does not support controlPagedResultResponse, call LDAP Wrapper directly instead.');
990 990
 			} else {
@@ -1002,7 +1002,7 @@  discard block
 block discarded – undo
1002 1002
 			$this->connection->resetConnectionResource();
1003 1003
 			$cr = $this->connection->getConnectionResource();
1004 1004
 
1005
-			if(!$this->ldap->isResource($cr)) {
1005
+			if (!$this->ldap->isResource($cr)) {
1006 1006
 				// Seems like we didn't find any resource.
1007 1007
 				\OCP\Util::writeLog('user_ldap', "Could not $command, because resource is missing.", \OCP\Util::DEBUG);
1008 1008
 				throw $e;
@@ -1023,13 +1023,13 @@  discard block
 block discarded – undo
1023 1023
 	 * @throws \OC\ServerNotAvailableException
1024 1024
 	 */
1025 1025
 	private function executeSearch($filter, $base, &$attr = null, $limit = null, $offset = null) {
1026
-		if(!is_null($attr) && !is_array($attr)) {
1026
+		if (!is_null($attr) && !is_array($attr)) {
1027 1027
 			$attr = array(mb_strtolower($attr, 'UTF-8'));
1028 1028
 		}
1029 1029
 
1030 1030
 		// See if we have a resource, in case not cancel with message
1031 1031
 		$cr = $this->connection->getConnectionResource();
1032
-		if(!$this->ldap->isResource($cr)) {
1032
+		if (!$this->ldap->isResource($cr)) {
1033 1033
 			// Seems like we didn't find any resource.
1034 1034
 			// Return an empty array just like before.
1035 1035
 			\OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG);
@@ -1043,7 +1043,7 @@  discard block
 block discarded – undo
1043 1043
 		$sr = $this->invokeLDAPMethod('search', $linkResources, $base, $filter, $attr);
1044 1044
 		// cannot use $cr anymore, might have changed in the previous call!
1045 1045
 		$error = $this->ldap->errno($this->connection->getConnectionResource());
1046
-		if(!is_array($sr) || $error !== 0) {
1046
+		if (!is_array($sr) || $error !== 0) {
1047 1047
 			\OCP\Util::writeLog('user_ldap', 'Attempt for Paging?  '.print_r($pagedSearchOK, true), \OCP\Util::ERROR);
1048 1048
 			return false;
1049 1049
 		}
@@ -1066,26 +1066,26 @@  discard block
 block discarded – undo
1066 1066
 	 */
1067 1067
 	private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) {
1068 1068
 		$cookie = null;
1069
-		if($pagedSearchOK) {
1069
+		if ($pagedSearchOK) {
1070 1070
 			$cr = $this->connection->getConnectionResource();
1071
-			foreach($sr as $key => $res) {
1072
-				if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
1071
+			foreach ($sr as $key => $res) {
1072
+				if ($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) {
1073 1073
 					$this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie);
1074 1074
 				}
1075 1075
 			}
1076 1076
 
1077 1077
 			//browsing through prior pages to get the cookie for the new one
1078
-			if($skipHandling) {
1078
+			if ($skipHandling) {
1079 1079
 				return false;
1080 1080
 			}
1081 1081
 			// if count is bigger, then the server does not support
1082 1082
 			// paged search. Instead, he did a normal search. We set a
1083 1083
 			// flag here, so the callee knows how to deal with it.
1084
-			if($iFoundItems <= $limit) {
1084
+			if ($iFoundItems <= $limit) {
1085 1085
 				$this->pagedSearchedSuccessful = true;
1086 1086
 			}
1087 1087
 		} else {
1088
-			if(!is_null($limit)) {
1088
+			if (!is_null($limit)) {
1089 1089
 				\OCP\Util::writeLog('user_ldap', 'Paged search was not available', \OCP\Util::INFO);
1090 1090
 			}
1091 1091
 		}
@@ -1114,7 +1114,7 @@  discard block
 block discarded – undo
1114 1114
 		\OCP\Util::writeLog('user_ldap', 'Count filter:  '.print_r($filter, true), \OCP\Util::DEBUG);
1115 1115
 
1116 1116
 		$limitPerPage = intval($this->connection->ldapPagingSize);
1117
-		if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1117
+		if (!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1118 1118
 			$limitPerPage = $limit;
1119 1119
 		}
1120 1120
 
@@ -1125,7 +1125,7 @@  discard block
 block discarded – undo
1125 1125
 		do {
1126 1126
 			$search = $this->executeSearch($filter, $base, $attr,
1127 1127
 										   $limitPerPage, $offset);
1128
-			if($search === false) {
1128
+			if ($search === false) {
1129 1129
 				return $counter > 0 ? $counter : false;
1130 1130
 			}
1131 1131
 			list($sr, $pagedSearchOK) = $search;
@@ -1144,7 +1144,7 @@  discard block
 block discarded – undo
1144 1144
 			 * Continue now depends on $hasMorePages value
1145 1145
 			 */
1146 1146
 			$continue = $pagedSearchOK && $hasMorePages;
1147
-		} while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
1147
+		} while ($continue && (is_null($limit) || $limit <= 0 || $limit > $counter));
1148 1148
 
1149 1149
 		return $counter;
1150 1150
 	}
@@ -1156,7 +1156,7 @@  discard block
 block discarded – undo
1156 1156
 	private function countEntriesInSearchResults($searchResults) {
1157 1157
 		$counter = 0;
1158 1158
 
1159
-		foreach($searchResults as $res) {
1159
+		foreach ($searchResults as $res) {
1160 1160
 			$count = intval($this->invokeLDAPMethod('countEntries', $this->connection->getConnectionResource(), $res));
1161 1161
 			$counter += $count;
1162 1162
 		}
@@ -1176,7 +1176,7 @@  discard block
 block discarded – undo
1176 1176
 	 */
1177 1177
 	public function search($filter, $base, $attr = null, $limit = null, $offset = null, $skipHandling = false) {
1178 1178
 		$limitPerPage = intval($this->connection->ldapPagingSize);
1179
-		if(!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1179
+		if (!is_null($limit) && $limit < $limitPerPage && $limit > 0) {
1180 1180
 			$limitPerPage = $limit;
1181 1181
 		}
1182 1182
 
@@ -1190,13 +1190,13 @@  discard block
 block discarded – undo
1190 1190
 		$savedoffset = $offset;
1191 1191
 		do {
1192 1192
 			$search = $this->executeSearch($filter, $base, $attr, $limitPerPage, $offset);
1193
-			if($search === false) {
1193
+			if ($search === false) {
1194 1194
 				return array();
1195 1195
 			}
1196 1196
 			list($sr, $pagedSearchOK) = $search;
1197 1197
 			$cr = $this->connection->getConnectionResource();
1198 1198
 
1199
-			if($skipHandling) {
1199
+			if ($skipHandling) {
1200 1200
 				//i.e. result do not need to be fetched, we just need the cookie
1201 1201
 				//thus pass 1 or any other value as $iFoundItems because it is not
1202 1202
 				//used
@@ -1207,7 +1207,7 @@  discard block
 block discarded – undo
1207 1207
 			}
1208 1208
 
1209 1209
 			$iFoundItems = 0;
1210
-			foreach($sr as $res) {
1210
+			foreach ($sr as $res) {
1211 1211
 				$findings = array_merge($findings, $this->invokeLDAPMethod('getEntries', $cr, $res));
1212 1212
 				$iFoundItems = max($iFoundItems, $findings['count']);
1213 1213
 				unset($findings['count']);
@@ -1223,25 +1223,25 @@  discard block
 block discarded – undo
1223 1223
 
1224 1224
 		// if we're here, probably no connection resource is returned.
1225 1225
 		// to make Nextcloud behave nicely, we simply give back an empty array.
1226
-		if(is_null($findings)) {
1226
+		if (is_null($findings)) {
1227 1227
 			return array();
1228 1228
 		}
1229 1229
 
1230
-		if(!is_null($attr)) {
1230
+		if (!is_null($attr)) {
1231 1231
 			$selection = array();
1232 1232
 			$i = 0;
1233
-			foreach($findings as $item) {
1234
-				if(!is_array($item)) {
1233
+			foreach ($findings as $item) {
1234
+				if (!is_array($item)) {
1235 1235
 					continue;
1236 1236
 				}
1237 1237
 				$item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8');
1238
-				foreach($attr as $key) {
1238
+				foreach ($attr as $key) {
1239 1239
 					$key = mb_strtolower($key, 'UTF-8');
1240
-					if(isset($item[$key])) {
1241
-						if(is_array($item[$key]) && isset($item[$key]['count'])) {
1240
+					if (isset($item[$key])) {
1241
+						if (is_array($item[$key]) && isset($item[$key]['count'])) {
1242 1242
 							unset($item[$key]['count']);
1243 1243
 						}
1244
-						if($key !== 'dn') {
1244
+						if ($key !== 'dn') {
1245 1245
 							$selection[$i][$key] = $this->resemblesDN($key) ?
1246 1246
 								$this->helper->sanitizeDN($item[$key])
1247 1247
 								: $key === 'objectguid' || $key === 'guid' ?
@@ -1260,7 +1260,7 @@  discard block
 block discarded – undo
1260 1260
 		//we slice the findings, when
1261 1261
 		//a) paged search unsuccessful, though attempted
1262 1262
 		//b) no paged search, but limit set
1263
-		if((!$this->getPagedSearchResultState()
1263
+		if ((!$this->getPagedSearchResultState()
1264 1264
 			&& $pagedSearchOK)
1265 1265
 			|| (
1266 1266
 				!$pagedSearchOK
@@ -1277,7 +1277,7 @@  discard block
 block discarded – undo
1277 1277
 	 * @return bool|mixed|string
1278 1278
 	 */
1279 1279
 	public function sanitizeUsername($name) {
1280
-		if($this->connection->ldapIgnoreNamingRules) {
1280
+		if ($this->connection->ldapIgnoreNamingRules) {
1281 1281
 			return $name;
1282 1282
 		}
1283 1283
 
@@ -1302,13 +1302,13 @@  discard block
 block discarded – undo
1302 1302
 	*/
1303 1303
 	public function escapeFilterPart($input, $allowAsterisk = false) {
1304 1304
 		$asterisk = '';
1305
-		if($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1305
+		if ($allowAsterisk && strlen($input) > 0 && $input[0] === '*') {
1306 1306
 			$asterisk = '*';
1307 1307
 			$input = mb_substr($input, 1, null, 'UTF-8');
1308 1308
 		}
1309 1309
 		$search  = array('*', '\\', '(', ')');
1310 1310
 		$replace = array('\\*', '\\\\', '\\(', '\\)');
1311
-		return $asterisk . str_replace($search, $replace, $input);
1311
+		return $asterisk.str_replace($search, $replace, $input);
1312 1312
 	}
1313 1313
 
1314 1314
 	/**
@@ -1338,13 +1338,13 @@  discard block
 block discarded – undo
1338 1338
 	 */
1339 1339
 	private function combineFilter($filters, $operator) {
1340 1340
 		$combinedFilter = '('.$operator;
1341
-		foreach($filters as $filter) {
1341
+		foreach ($filters as $filter) {
1342 1342
 			if ($filter !== '' && $filter[0] !== '(') {
1343 1343
 				$filter = '('.$filter.')';
1344 1344
 			}
1345
-			$combinedFilter.=$filter;
1345
+			$combinedFilter .= $filter;
1346 1346
 		}
1347
-		$combinedFilter.=')';
1347
+		$combinedFilter .= ')';
1348 1348
 		return $combinedFilter;
1349 1349
 	}
1350 1350
 
@@ -1380,17 +1380,17 @@  discard block
 block discarded – undo
1380 1380
 	 * @throws \Exception
1381 1381
 	 */
1382 1382
 	private function getAdvancedFilterPartForSearch($search, $searchAttributes) {
1383
-		if(!is_array($searchAttributes) || count($searchAttributes) < 2) {
1383
+		if (!is_array($searchAttributes) || count($searchAttributes) < 2) {
1384 1384
 			throw new \Exception('searchAttributes must be an array with at least two string');
1385 1385
 		}
1386 1386
 		$searchWords = explode(' ', trim($search));
1387 1387
 		$wordFilters = array();
1388
-		foreach($searchWords as $word) {
1388
+		foreach ($searchWords as $word) {
1389 1389
 			$word = $this->prepareSearchTerm($word);
1390 1390
 			//every word needs to appear at least once
1391 1391
 			$wordMatchOneAttrFilters = array();
1392
-			foreach($searchAttributes as $attr) {
1393
-				$wordMatchOneAttrFilters[] = $attr . '=' . $word;
1392
+			foreach ($searchAttributes as $attr) {
1393
+				$wordMatchOneAttrFilters[] = $attr.'='.$word;
1394 1394
 			}
1395 1395
 			$wordFilters[] = $this->combineFilterWithOr($wordMatchOneAttrFilters);
1396 1396
 		}
@@ -1408,10 +1408,10 @@  discard block
 block discarded – undo
1408 1408
 	private function getFilterPartForSearch($search, $searchAttributes, $fallbackAttribute) {
1409 1409
 		$filter = array();
1410 1410
 		$haveMultiSearchAttributes = (is_array($searchAttributes) && count($searchAttributes) > 0);
1411
-		if($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1411
+		if ($haveMultiSearchAttributes && strpos(trim($search), ' ') !== false) {
1412 1412
 			try {
1413 1413
 				return $this->getAdvancedFilterPartForSearch($search, $searchAttributes);
1414
-			} catch(\Exception $e) {
1414
+			} catch (\Exception $e) {
1415 1415
 				\OCP\Util::writeLog(
1416 1416
 					'user_ldap',
1417 1417
 					'Creating advanced filter for search failed, falling back to simple method.',
@@ -1421,17 +1421,17 @@  discard block
 block discarded – undo
1421 1421
 		}
1422 1422
 
1423 1423
 		$search = $this->prepareSearchTerm($search);
1424
-		if(!is_array($searchAttributes) || count($searchAttributes) === 0) {
1424
+		if (!is_array($searchAttributes) || count($searchAttributes) === 0) {
1425 1425
 			if ($fallbackAttribute === '') {
1426 1426
 				return '';
1427 1427
 			}
1428
-			$filter[] = $fallbackAttribute . '=' . $search;
1428
+			$filter[] = $fallbackAttribute.'='.$search;
1429 1429
 		} else {
1430
-			foreach($searchAttributes as $attribute) {
1431
-				$filter[] = $attribute . '=' . $search;
1430
+			foreach ($searchAttributes as $attribute) {
1431
+				$filter[] = $attribute.'='.$search;
1432 1432
 			}
1433 1433
 		}
1434
-		if(count($filter) === 1) {
1434
+		if (count($filter) === 1) {
1435 1435
 			return '('.$filter[0].')';
1436 1436
 		}
1437 1437
 		return $this->combineFilterWithOr($filter);
@@ -1452,7 +1452,7 @@  discard block
 block discarded – undo
1452 1452
 		if ($term === '') {
1453 1453
 			$result = '*';
1454 1454
 		} else if ($allowEnum !== 'no') {
1455
-			$result = $term . '*';
1455
+			$result = $term.'*';
1456 1456
 		}
1457 1457
 		return $result;
1458 1458
 	}
@@ -1464,7 +1464,7 @@  discard block
 block discarded – undo
1464 1464
 	public function getFilterForUserCount() {
1465 1465
 		$filter = $this->combineFilterWithAnd(array(
1466 1466
 			$this->connection->ldapUserFilter,
1467
-			$this->connection->ldapUserDisplayName . '=*'
1467
+			$this->connection->ldapUserDisplayName.'=*'
1468 1468
 		));
1469 1469
 
1470 1470
 		return $filter;
@@ -1482,7 +1482,7 @@  discard block
 block discarded – undo
1482 1482
 			'ldapAgentName' => $name,
1483 1483
 			'ldapAgentPassword' => $password
1484 1484
 		);
1485
-		if(!$testConnection->setConfiguration($credentials)) {
1485
+		if (!$testConnection->setConfiguration($credentials)) {
1486 1486
 			return false;
1487 1487
 		}
1488 1488
 		return $testConnection->bind();
@@ -1504,30 +1504,30 @@  discard block
 block discarded – undo
1504 1504
 			// Sacrebleu! The UUID attribute is unknown :( We need first an
1505 1505
 			// existing DN to be able to reliably detect it.
1506 1506
 			$result = $this->search($filter, $base, ['dn'], 1);
1507
-			if(!isset($result[0]) || !isset($result[0]['dn'])) {
1507
+			if (!isset($result[0]) || !isset($result[0]['dn'])) {
1508 1508
 				throw new \Exception('Cannot determine UUID attribute');
1509 1509
 			}
1510 1510
 			$dn = $result[0]['dn'][0];
1511
-			if(!$this->detectUuidAttribute($dn, true)) {
1511
+			if (!$this->detectUuidAttribute($dn, true)) {
1512 1512
 				throw new \Exception('Cannot determine UUID attribute');
1513 1513
 			}
1514 1514
 		} else {
1515 1515
 			// The UUID attribute is either known or an override is given.
1516 1516
 			// By calling this method we ensure that $this->connection->$uuidAttr
1517 1517
 			// is definitely set
1518
-			if(!$this->detectUuidAttribute('', true)) {
1518
+			if (!$this->detectUuidAttribute('', true)) {
1519 1519
 				throw new \Exception('Cannot determine UUID attribute');
1520 1520
 			}
1521 1521
 		}
1522 1522
 
1523 1523
 		$uuidAttr = $this->connection->ldapUuidUserAttribute;
1524
-		if($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1524
+		if ($uuidAttr === 'guid' || $uuidAttr === 'objectguid') {
1525 1525
 			$uuid = $this->formatGuid2ForFilterUser($uuid);
1526 1526
 		}
1527 1527
 
1528
-		$filter = $uuidAttr . '=' . $uuid;
1528
+		$filter = $uuidAttr.'='.$uuid;
1529 1529
 		$result = $this->searchUsers($filter, ['dn'], 2);
1530
-		if(is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1530
+		if (is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) {
1531 1531
 			// we put the count into account to make sure that this is
1532 1532
 			// really unique
1533 1533
 			return $result[0]['dn'][0];
@@ -1546,7 +1546,7 @@  discard block
 block discarded – undo
1546 1546
 	 * @return bool true on success, false otherwise
1547 1547
 	 */
1548 1548
 	private function detectUuidAttribute($dn, $isUser = true, $force = false, array $ldapRecord = null) {
1549
-		if($isUser) {
1549
+		if ($isUser) {
1550 1550
 			$uuidAttr     = 'ldapUuidUserAttribute';
1551 1551
 			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1552 1552
 		} else {
@@ -1554,7 +1554,7 @@  discard block
 block discarded – undo
1554 1554
 			$uuidOverride = $this->connection->ldapExpertUUIDGroupAttr;
1555 1555
 		}
1556 1556
 
1557
-		if(($this->connection->$uuidAttr !== 'auto') && !$force) {
1557
+		if (($this->connection->$uuidAttr !== 'auto') && !$force) {
1558 1558
 			return true;
1559 1559
 		}
1560 1560
 
@@ -1563,10 +1563,10 @@  discard block
 block discarded – undo
1563 1563
 			return true;
1564 1564
 		}
1565 1565
 
1566
-		foreach(self::UUID_ATTRIBUTES as $attribute) {
1567
-			if($ldapRecord !== null) {
1566
+		foreach (self::UUID_ATTRIBUTES as $attribute) {
1567
+			if ($ldapRecord !== null) {
1568 1568
 				// we have the info from LDAP already, we don't need to talk to the server again
1569
-				if(isset($ldapRecord[$attribute])) {
1569
+				if (isset($ldapRecord[$attribute])) {
1570 1570
 					$this->connection->$uuidAttr = $attribute;
1571 1571
 					return true;
1572 1572
 				} else {
@@ -1575,7 +1575,7 @@  discard block
 block discarded – undo
1575 1575
 			}
1576 1576
 
1577 1577
 			$value = $this->readAttribute($dn, $attribute);
1578
-			if(is_array($value) && isset($value[0]) && !empty($value[0])) {
1578
+			if (is_array($value) && isset($value[0]) && !empty($value[0])) {
1579 1579
 				\OCP\Util::writeLog('user_ldap',
1580 1580
 									'Setting '.$attribute.' as '.$uuidAttr,
1581 1581
 									\OCP\Util::DEBUG);
@@ -1597,7 +1597,7 @@  discard block
 block discarded – undo
1597 1597
 	 * @return bool|string
1598 1598
 	 */
1599 1599
 	public function getUUID($dn, $isUser = true, $ldapRecord = null) {
1600
-		if($isUser) {
1600
+		if ($isUser) {
1601 1601
 			$uuidAttr     = 'ldapUuidUserAttribute';
1602 1602
 			$uuidOverride = $this->connection->ldapExpertUUIDUserAttr;
1603 1603
 		} else {
@@ -1606,10 +1606,10 @@  discard block
 block discarded – undo
1606 1606
 		}
1607 1607
 
1608 1608
 		$uuid = false;
1609
-		if($this->detectUuidAttribute($dn, $isUser, false, $ldapRecord)) {
1609
+		if ($this->detectUuidAttribute($dn, $isUser, false, $ldapRecord)) {
1610 1610
 			$attr = $this->connection->$uuidAttr;
1611 1611
 			$uuid = isset($ldapRecord[$attr]) ? $ldapRecord[$attr] : $this->readAttribute($dn, $attr);
1612
-			if( !is_array($uuid)
1612
+			if (!is_array($uuid)
1613 1613
 				&& $uuidOverride !== ''
1614 1614
 				&& $this->detectUuidAttribute($dn, $isUser, true, $ldapRecord))
1615 1615
 			{
@@ -1617,7 +1617,7 @@  discard block
 block discarded – undo
1617 1617
 					? $ldapRecord[$this->connection->$uuidAttr]
1618 1618
 					: $this->readAttribute($dn, $this->connection->$uuidAttr);
1619 1619
 			}
1620
-			if(is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1620
+			if (is_array($uuid) && isset($uuid[0]) && !empty($uuid[0])) {
1621 1621
 				$uuid = $uuid[0];
1622 1622
 			}
1623 1623
 		}
@@ -1634,19 +1634,19 @@  discard block
 block discarded – undo
1634 1634
 	private function convertObjectGUID2Str($oguid) {
1635 1635
 		$hex_guid = bin2hex($oguid);
1636 1636
 		$hex_guid_to_guid_str = '';
1637
-		for($k = 1; $k <= 4; ++$k) {
1637
+		for ($k = 1; $k <= 4; ++$k) {
1638 1638
 			$hex_guid_to_guid_str .= substr($hex_guid, 8 - 2 * $k, 2);
1639 1639
 		}
1640 1640
 		$hex_guid_to_guid_str .= '-';
1641
-		for($k = 1; $k <= 2; ++$k) {
1641
+		for ($k = 1; $k <= 2; ++$k) {
1642 1642
 			$hex_guid_to_guid_str .= substr($hex_guid, 12 - 2 * $k, 2);
1643 1643
 		}
1644 1644
 		$hex_guid_to_guid_str .= '-';
1645
-		for($k = 1; $k <= 2; ++$k) {
1645
+		for ($k = 1; $k <= 2; ++$k) {
1646 1646
 			$hex_guid_to_guid_str .= substr($hex_guid, 16 - 2 * $k, 2);
1647 1647
 		}
1648
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 16, 4);
1649
-		$hex_guid_to_guid_str .= '-' . substr($hex_guid, 20);
1648
+		$hex_guid_to_guid_str .= '-'.substr($hex_guid, 16, 4);
1649
+		$hex_guid_to_guid_str .= '-'.substr($hex_guid, 20);
1650 1650
 
1651 1651
 		return strtoupper($hex_guid_to_guid_str);
1652 1652
 	}
@@ -1663,11 +1663,11 @@  discard block
 block discarded – undo
1663 1663
 	 * @return string
1664 1664
 	 */
1665 1665
 	public function formatGuid2ForFilterUser($guid) {
1666
-		if(!is_string($guid)) {
1666
+		if (!is_string($guid)) {
1667 1667
 			throw new \InvalidArgumentException('String expected');
1668 1668
 		}
1669 1669
 		$blocks = explode('-', $guid);
1670
-		if(count($blocks) !== 5) {
1670
+		if (count($blocks) !== 5) {
1671 1671
 			/*
1672 1672
 			 * Why not throw an Exception instead? This method is a utility
1673 1673
 			 * called only when trying to figure out whether a "missing" known
@@ -1680,20 +1680,20 @@  discard block
 block discarded – undo
1680 1680
 			 * user. Instead we write a log message.
1681 1681
 			 */
1682 1682
 			\OC::$server->getLogger()->info(
1683
-				'Passed string does not resemble a valid GUID. Known UUID ' .
1683
+				'Passed string does not resemble a valid GUID. Known UUID '.
1684 1684
 				'({uuid}) probably does not match UUID configuration.',
1685
-				[ 'app' => 'user_ldap', 'uuid' => $guid ]
1685
+				['app' => 'user_ldap', 'uuid' => $guid]
1686 1686
 			);
1687 1687
 			return $guid;
1688 1688
 		}
1689
-		for($i=0; $i < 3; $i++) {
1689
+		for ($i = 0; $i < 3; $i++) {
1690 1690
 			$pairs = str_split($blocks[$i], 2);
1691 1691
 			$pairs = array_reverse($pairs);
1692 1692
 			$blocks[$i] = implode('', $pairs);
1693 1693
 		}
1694
-		for($i=0; $i < 5; $i++) {
1694
+		for ($i = 0; $i < 5; $i++) {
1695 1695
 			$pairs = str_split($blocks[$i], 2);
1696
-			$blocks[$i] = '\\' . implode('\\', $pairs);
1696
+			$blocks[$i] = '\\'.implode('\\', $pairs);
1697 1697
 		}
1698 1698
 		return implode('', $blocks);
1699 1699
 	}
@@ -1707,12 +1707,12 @@  discard block
 block discarded – undo
1707 1707
 		$domainDN = $this->getDomainDNFromDN($dn);
1708 1708
 		$cacheKey = 'getSID-'.$domainDN;
1709 1709
 		$sid = $this->connection->getFromCache($cacheKey);
1710
-		if(!is_null($sid)) {
1710
+		if (!is_null($sid)) {
1711 1711
 			return $sid;
1712 1712
 		}
1713 1713
 
1714 1714
 		$objectSid = $this->readAttribute($domainDN, 'objectsid');
1715
-		if(!is_array($objectSid) || empty($objectSid)) {
1715
+		if (!is_array($objectSid) || empty($objectSid)) {
1716 1716
 			$this->connection->writeToCache($cacheKey, false);
1717 1717
 			return false;
1718 1718
 		}
@@ -1770,12 +1770,12 @@  discard block
 block discarded – undo
1770 1770
 		$belongsToBase = false;
1771 1771
 		$bases = $this->helper->sanitizeDN($bases);
1772 1772
 
1773
-		foreach($bases as $base) {
1773
+		foreach ($bases as $base) {
1774 1774
 			$belongsToBase = true;
1775
-			if(mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($base, 'UTF-8'))) {
1775
+			if (mb_strripos($dn, $base, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8') - mb_strlen($base, 'UTF-8'))) {
1776 1776
 				$belongsToBase = false;
1777 1777
 			}
1778
-			if($belongsToBase) {
1778
+			if ($belongsToBase) {
1779 1779
 				break;
1780 1780
 			}
1781 1781
 		}
@@ -1786,7 +1786,7 @@  discard block
 block discarded – undo
1786 1786
 	 * resets a running Paged Search operation
1787 1787
 	 */
1788 1788
 	private function abandonPagedSearch() {
1789
-		if($this->connection->hasPagedResultSupport) {
1789
+		if ($this->connection->hasPagedResultSupport) {
1790 1790
 			$cr = $this->connection->getConnectionResource();
1791 1791
 			$this->invokeLDAPMethod('controlPagedResult', $cr, 0, false, $this->lastCookie);
1792 1792
 			$this->getPagedSearchResultState();
@@ -1804,16 +1804,16 @@  discard block
 block discarded – undo
1804 1804
 	 * @return string containing the key or empty if none is cached
1805 1805
 	 */
1806 1806
 	private function getPagedResultCookie($base, $filter, $limit, $offset) {
1807
-		if($offset === 0) {
1807
+		if ($offset === 0) {
1808 1808
 			return '';
1809 1809
 		}
1810 1810
 		$offset -= $limit;
1811 1811
 		//we work with cache here
1812
-		$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' . intval($limit) . '-' . intval($offset);
1812
+		$cacheKey = 'lc'.crc32($base).'-'.crc32($filter).'-'.intval($limit).'-'.intval($offset);
1813 1813
 		$cookie = '';
1814
-		if(isset($this->cookies[$cacheKey])) {
1814
+		if (isset($this->cookies[$cacheKey])) {
1815 1815
 			$cookie = $this->cookies[$cacheKey];
1816
-			if(is_null($cookie)) {
1816
+			if (is_null($cookie)) {
1817 1817
 				$cookie = '';
1818 1818
 			}
1819 1819
 		}
@@ -1831,11 +1831,11 @@  discard block
 block discarded – undo
1831 1831
 	 * @return bool
1832 1832
 	 */
1833 1833
 	public function hasMoreResults() {
1834
-		if(!$this->connection->hasPagedResultSupport) {
1834
+		if (!$this->connection->hasPagedResultSupport) {
1835 1835
 			return false;
1836 1836
 		}
1837 1837
 
1838
-		if(empty($this->lastCookie) && $this->lastCookie !== '0') {
1838
+		if (empty($this->lastCookie) && $this->lastCookie !== '0') {
1839 1839
 			// as in RFC 2696, when all results are returned, the cookie will
1840 1840
 			// be empty.
1841 1841
 			return false;
@@ -1855,8 +1855,8 @@  discard block
 block discarded – undo
1855 1855
 	 */
1856 1856
 	private function setPagedResultCookie($base, $filter, $limit, $offset, $cookie) {
1857 1857
 		// allow '0' for 389ds
1858
-		if(!empty($cookie) || $cookie === '0') {
1859
-			$cacheKey = 'lc' . crc32($base) . '-' . crc32($filter) . '-' .intval($limit) . '-' . intval($offset);
1858
+		if (!empty($cookie) || $cookie === '0') {
1859
+			$cacheKey = 'lc'.crc32($base).'-'.crc32($filter).'-'.intval($limit).'-'.intval($offset);
1860 1860
 			$this->cookies[$cacheKey] = $cookie;
1861 1861
 			$this->lastCookie = $cookie;
1862 1862
 		}
@@ -1883,22 +1883,22 @@  discard block
 block discarded – undo
1883 1883
 	 */
1884 1884
 	private function initPagedSearch($filter, $bases, $attr, $limit, $offset) {
1885 1885
 		$pagedSearchOK = false;
1886
-		if($this->connection->hasPagedResultSupport && ($limit !== 0)) {
1886
+		if ($this->connection->hasPagedResultSupport && ($limit !== 0)) {
1887 1887
 			$offset = intval($offset); //can be null
1888 1888
 			\OCP\Util::writeLog('user_ldap',
1889 1889
 				'initializing paged search for  Filter '.$filter.' base '.print_r($bases, true)
1890
-				.' attr '.print_r($attr, true). ' limit ' .$limit.' offset '.$offset,
1890
+				.' attr '.print_r($attr, true).' limit '.$limit.' offset '.$offset,
1891 1891
 				\OCP\Util::DEBUG);
1892 1892
 			//get the cookie from the search for the previous search, required by LDAP
1893
-			foreach($bases as $base) {
1893
+			foreach ($bases as $base) {
1894 1894
 
1895 1895
 				$cookie = $this->getPagedResultCookie($base, $filter, $limit, $offset);
1896
-				if(empty($cookie) && $cookie !== "0" && ($offset > 0)) {
1896
+				if (empty($cookie) && $cookie !== "0" && ($offset > 0)) {
1897 1897
 					// no cookie known from a potential previous search. We need
1898 1898
 					// to start from 0 to come to the desired page. cookie value
1899 1899
 					// of '0' is valid, because 389ds
1900 1900
 					$reOffset = 0;
1901
-					while($reOffset < $offset) {
1901
+					while ($reOffset < $offset) {
1902 1902
 						$this->search($filter, array($base), $attr, $limit, $reOffset, true);
1903 1903
 						$reOffset += $limit;
1904 1904
 					}
@@ -1906,17 +1906,17 @@  discard block
 block discarded – undo
1906 1906
 					//still no cookie? obviously, the server does not like us. Let's skip paging efforts.
1907 1907
 					// '0' is valid, because 389ds
1908 1908
 					//TODO: remember this, probably does not change in the next request...
1909
-					if(empty($cookie) && $cookie !== '0') {
1909
+					if (empty($cookie) && $cookie !== '0') {
1910 1910
 						$cookie = null;
1911 1911
 					}
1912 1912
 				}
1913
-				if(!is_null($cookie)) {
1913
+				if (!is_null($cookie)) {
1914 1914
 					//since offset = 0, this is a new search. We abandon other searches that might be ongoing.
1915 1915
 					$this->abandonPagedSearch();
1916 1916
 					$pagedSearchOK = $this->invokeLDAPMethod('controlPagedResult',
1917 1917
 						$this->connection->getConnectionResource(), $limit,
1918 1918
 						false, $cookie);
1919
-					if(!$pagedSearchOK) {
1919
+					if (!$pagedSearchOK) {
1920 1920
 						return false;
1921 1921
 					}
1922 1922
 					\OCP\Util::writeLog('user_ldap', 'Ready for a paged search', \OCP\Util::DEBUG);
@@ -1933,7 +1933,7 @@  discard block
 block discarded – undo
1933 1933
 		 * So we added "&& !empty($this->lastCookie)" to this test to ignore pagination
1934 1934
 		 * if we don't have a previous paged search.
1935 1935
 		 */
1936
-		} else if($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) {
1936
+		} else if ($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) {
1937 1937
 			// a search without limit was requested. However, if we do use
1938 1938
 			// Paged Search once, we always must do it. This requires us to
1939 1939
 			// initialize it with the configured page size.
Please login to merge, or discard this patch.
apps/user_ldap/lib/Proxy.php 1 patch
Indentation   +166 added lines, -166 removed lines patch added patch discarded remove patch
@@ -35,170 +35,170 @@
 block discarded – undo
35 35
 use OCA\User_LDAP\User\Manager;
36 36
 
37 37
 abstract class Proxy {
38
-	static private $accesses = array();
39
-	private $ldap = null;
40
-
41
-	/** @var \OCP\ICache|null */
42
-	private $cache;
43
-
44
-	/**
45
-	 * @param ILDAPWrapper $ldap
46
-	 */
47
-	public function __construct(ILDAPWrapper $ldap) {
48
-		$this->ldap = $ldap;
49
-		$memcache = \OC::$server->getMemCacheFactory();
50
-		if($memcache->isAvailable()) {
51
-			$this->cache = $memcache->create();
52
-		}
53
-	}
54
-
55
-	/**
56
-	 * @param string $configPrefix
57
-	 */
58
-	private function addAccess($configPrefix) {
59
-		static $ocConfig;
60
-		static $fs;
61
-		static $log;
62
-		static $avatarM;
63
-		static $userMap;
64
-		static $groupMap;
65
-		static $db;
66
-		static $coreUserManager;
67
-		static $coreNotificationManager;
68
-		if($fs === null) {
69
-			$ocConfig = \OC::$server->getConfig();
70
-			$fs       = new FilesystemHelper();
71
-			$log      = new LogWrapper();
72
-			$avatarM  = \OC::$server->getAvatarManager();
73
-			$db       = \OC::$server->getDatabaseConnection();
74
-			$userMap  = new UserMapping($db);
75
-			$groupMap = new GroupMapping($db);
76
-			$coreUserManager = \OC::$server->getUserManager();
77
-			$coreNotificationManager = \OC::$server->getNotificationManager();
78
-		}
79
-		$userManager =
80
-			new Manager($ocConfig, $fs, $log, $avatarM, new \OCP\Image(), $db,
81
-				$coreUserManager, $coreNotificationManager);
82
-		$connector = new Connection($this->ldap, $configPrefix);
83
-		$access = new Access($connector, $this->ldap, $userManager, new Helper(\OC::$server->getConfig()), \OC::$server);
84
-		$access->setUserMapper($userMap);
85
-		$access->setGroupMapper($groupMap);
86
-		self::$accesses[$configPrefix] = $access;
87
-	}
88
-
89
-	/**
90
-	 * @param string $configPrefix
91
-	 * @return mixed
92
-	 */
93
-	protected function getAccess($configPrefix) {
94
-		if(!isset(self::$accesses[$configPrefix])) {
95
-			$this->addAccess($configPrefix);
96
-		}
97
-		return self::$accesses[$configPrefix];
98
-	}
99
-
100
-	/**
101
-	 * @param string $uid
102
-	 * @return string
103
-	 */
104
-	protected function getUserCacheKey($uid) {
105
-		return 'user-'.$uid.'-lastSeenOn';
106
-	}
107
-
108
-	/**
109
-	 * @param string $gid
110
-	 * @return string
111
-	 */
112
-	protected function getGroupCacheKey($gid) {
113
-		return 'group-'.$gid.'-lastSeenOn';
114
-	}
115
-
116
-	/**
117
-	 * @param string $id
118
-	 * @param string $method
119
-	 * @param array $parameters
120
-	 * @param bool $passOnWhen
121
-	 * @return mixed
122
-	 */
123
-	abstract protected function callOnLastSeenOn($id, $method, $parameters, $passOnWhen);
124
-
125
-	/**
126
-	 * @param string $id
127
-	 * @param string $method
128
-	 * @param array $parameters
129
-	 * @return mixed
130
-	 */
131
-	abstract protected function walkBackends($id, $method, $parameters);
132
-
133
-	/**
134
-	 * @param string $id
135
-	 * @return Access
136
-	 */
137
-	abstract public function getLDAPAccess($id);
138
-
139
-	/**
140
-	 * Takes care of the request to the User backend
141
-	 * @param string $id
142
-	 * @param string $method string, the method of the user backend that shall be called
143
-	 * @param array $parameters an array of parameters to be passed
144
-	 * @param bool $passOnWhen
145
-	 * @return mixed, the result of the specified method
146
-	 */
147
-	protected function handleRequest($id, $method, $parameters, $passOnWhen = false) {
148
-		$result = $this->callOnLastSeenOn($id,  $method, $parameters, $passOnWhen);
149
-		if($result === $passOnWhen) {
150
-			$result = $this->walkBackends($id, $method, $parameters);
151
-		}
152
-		return $result;
153
-	}
154
-
155
-	/**
156
-	 * @param string|null $key
157
-	 * @return string
158
-	 */
159
-	private function getCacheKey($key) {
160
-		$prefix = 'LDAP-Proxy-';
161
-		if($key === null) {
162
-			return $prefix;
163
-		}
164
-		return $prefix.md5($key);
165
-	}
166
-
167
-	/**
168
-	 * @param string $key
169
-	 * @return mixed|null
170
-	 */
171
-	public function getFromCache($key) {
172
-		if($this->cache === null) {
173
-			return null;
174
-		}
175
-
176
-		$key = $this->getCacheKey($key);
177
-		$value = $this->cache->get($key);
178
-		if ($value === null) {
179
-			return null;
180
-		}
181
-
182
-		return json_decode(base64_decode($value));
183
-	}
184
-
185
-	/**
186
-	 * @param string $key
187
-	 * @param mixed $value
188
-	 */
189
-	public function writeToCache($key, $value) {
190
-		if($this->cache === null) {
191
-			return;
192
-		}
193
-		$key   = $this->getCacheKey($key);
194
-		$value = base64_encode(json_encode($value));
195
-		$this->cache->set($key, $value, 2592000);
196
-	}
197
-
198
-	public function clearCache() {
199
-		if($this->cache === null) {
200
-			return;
201
-		}
202
-		$this->cache->clear($this->getCacheKey(null));
203
-	}
38
+    static private $accesses = array();
39
+    private $ldap = null;
40
+
41
+    /** @var \OCP\ICache|null */
42
+    private $cache;
43
+
44
+    /**
45
+     * @param ILDAPWrapper $ldap
46
+     */
47
+    public function __construct(ILDAPWrapper $ldap) {
48
+        $this->ldap = $ldap;
49
+        $memcache = \OC::$server->getMemCacheFactory();
50
+        if($memcache->isAvailable()) {
51
+            $this->cache = $memcache->create();
52
+        }
53
+    }
54
+
55
+    /**
56
+     * @param string $configPrefix
57
+     */
58
+    private function addAccess($configPrefix) {
59
+        static $ocConfig;
60
+        static $fs;
61
+        static $log;
62
+        static $avatarM;
63
+        static $userMap;
64
+        static $groupMap;
65
+        static $db;
66
+        static $coreUserManager;
67
+        static $coreNotificationManager;
68
+        if($fs === null) {
69
+            $ocConfig = \OC::$server->getConfig();
70
+            $fs       = new FilesystemHelper();
71
+            $log      = new LogWrapper();
72
+            $avatarM  = \OC::$server->getAvatarManager();
73
+            $db       = \OC::$server->getDatabaseConnection();
74
+            $userMap  = new UserMapping($db);
75
+            $groupMap = new GroupMapping($db);
76
+            $coreUserManager = \OC::$server->getUserManager();
77
+            $coreNotificationManager = \OC::$server->getNotificationManager();
78
+        }
79
+        $userManager =
80
+            new Manager($ocConfig, $fs, $log, $avatarM, new \OCP\Image(), $db,
81
+                $coreUserManager, $coreNotificationManager);
82
+        $connector = new Connection($this->ldap, $configPrefix);
83
+        $access = new Access($connector, $this->ldap, $userManager, new Helper(\OC::$server->getConfig()), \OC::$server);
84
+        $access->setUserMapper($userMap);
85
+        $access->setGroupMapper($groupMap);
86
+        self::$accesses[$configPrefix] = $access;
87
+    }
88
+
89
+    /**
90
+     * @param string $configPrefix
91
+     * @return mixed
92
+     */
93
+    protected function getAccess($configPrefix) {
94
+        if(!isset(self::$accesses[$configPrefix])) {
95
+            $this->addAccess($configPrefix);
96
+        }
97
+        return self::$accesses[$configPrefix];
98
+    }
99
+
100
+    /**
101
+     * @param string $uid
102
+     * @return string
103
+     */
104
+    protected function getUserCacheKey($uid) {
105
+        return 'user-'.$uid.'-lastSeenOn';
106
+    }
107
+
108
+    /**
109
+     * @param string $gid
110
+     * @return string
111
+     */
112
+    protected function getGroupCacheKey($gid) {
113
+        return 'group-'.$gid.'-lastSeenOn';
114
+    }
115
+
116
+    /**
117
+     * @param string $id
118
+     * @param string $method
119
+     * @param array $parameters
120
+     * @param bool $passOnWhen
121
+     * @return mixed
122
+     */
123
+    abstract protected function callOnLastSeenOn($id, $method, $parameters, $passOnWhen);
124
+
125
+    /**
126
+     * @param string $id
127
+     * @param string $method
128
+     * @param array $parameters
129
+     * @return mixed
130
+     */
131
+    abstract protected function walkBackends($id, $method, $parameters);
132
+
133
+    /**
134
+     * @param string $id
135
+     * @return Access
136
+     */
137
+    abstract public function getLDAPAccess($id);
138
+
139
+    /**
140
+     * Takes care of the request to the User backend
141
+     * @param string $id
142
+     * @param string $method string, the method of the user backend that shall be called
143
+     * @param array $parameters an array of parameters to be passed
144
+     * @param bool $passOnWhen
145
+     * @return mixed, the result of the specified method
146
+     */
147
+    protected function handleRequest($id, $method, $parameters, $passOnWhen = false) {
148
+        $result = $this->callOnLastSeenOn($id,  $method, $parameters, $passOnWhen);
149
+        if($result === $passOnWhen) {
150
+            $result = $this->walkBackends($id, $method, $parameters);
151
+        }
152
+        return $result;
153
+    }
154
+
155
+    /**
156
+     * @param string|null $key
157
+     * @return string
158
+     */
159
+    private function getCacheKey($key) {
160
+        $prefix = 'LDAP-Proxy-';
161
+        if($key === null) {
162
+            return $prefix;
163
+        }
164
+        return $prefix.md5($key);
165
+    }
166
+
167
+    /**
168
+     * @param string $key
169
+     * @return mixed|null
170
+     */
171
+    public function getFromCache($key) {
172
+        if($this->cache === null) {
173
+            return null;
174
+        }
175
+
176
+        $key = $this->getCacheKey($key);
177
+        $value = $this->cache->get($key);
178
+        if ($value === null) {
179
+            return null;
180
+        }
181
+
182
+        return json_decode(base64_decode($value));
183
+    }
184
+
185
+    /**
186
+     * @param string $key
187
+     * @param mixed $value
188
+     */
189
+    public function writeToCache($key, $value) {
190
+        if($this->cache === null) {
191
+            return;
192
+        }
193
+        $key   = $this->getCacheKey($key);
194
+        $value = base64_encode(json_encode($value));
195
+        $this->cache->set($key, $value, 2592000);
196
+    }
197
+
198
+    public function clearCache() {
199
+        if($this->cache === null) {
200
+            return;
201
+        }
202
+        $this->cache->clear($this->getCacheKey(null));
203
+    }
204 204
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/User_LDAP.php 1 patch
Indentation   +511 added lines, -511 removed lines patch added patch discarded remove patch
@@ -47,518 +47,518 @@
 block discarded – undo
47 47
 use OCP\Util;
48 48
 
49 49
 class User_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserInterface, IUserLDAP {
50
-	/** @var \OCP\IConfig */
51
-	protected $ocConfig;
52
-
53
-	/** @var INotificationManager */
54
-	protected $notificationManager;
55
-
56
-	/** @var string */
57
-	protected $currentUserInDeletionProcess;
58
-
59
-	/**
60
-	 * @param Access $access
61
-	 * @param \OCP\IConfig $ocConfig
62
-	 * @param \OCP\Notification\IManager $notificationManager
63
-	 * @param IUserSession $userSession
64
-	 */
65
-	public function __construct(Access $access, IConfig $ocConfig, INotificationManager $notificationManager, IUserSession $userSession) {
66
-		parent::__construct($access);
67
-		$this->ocConfig = $ocConfig;
68
-		$this->notificationManager = $notificationManager;
69
-		$this->registerHooks($userSession);
70
-	}
71
-
72
-	protected function registerHooks(IUserSession $userSession) {
73
-		$userSession->listen('\OC\User', 'preDelete', [$this, 'preDeleteUser']);
74
-		$userSession->listen('\OC\User', 'postDelete', [$this, 'postDeleteUser']);
75
-	}
76
-
77
-	public function preDeleteUser(IUser $user) {
78
-		$this->currentUserInDeletionProcess = $user->getUID();
79
-	}
80
-
81
-	public function postDeleteUser() {
82
-		$this->currentUserInDeletionProcess = null;
83
-	}
84
-
85
-	/**
86
-	 * checks whether the user is allowed to change his avatar in Nextcloud
87
-	 * @param string $uid the Nextcloud user name
88
-	 * @return boolean either the user can or cannot
89
-	 */
90
-	public function canChangeAvatar($uid) {
91
-		$user = $this->access->userManager->get($uid);
92
-		if(!$user instanceof User) {
93
-			return false;
94
-		}
95
-		if($user->getAvatarImage() === false) {
96
-			return true;
97
-		}
98
-
99
-		return false;
100
-	}
101
-
102
-	/**
103
-	 * returns the username for the given login name, if available
104
-	 *
105
-	 * @param string $loginName
106
-	 * @return string|false
107
-	 */
108
-	public function loginName2UserName($loginName) {
109
-		$cacheKey = 'loginName2UserName-'.$loginName;
110
-		$username = $this->access->connection->getFromCache($cacheKey);
111
-		if(!is_null($username)) {
112
-			return $username;
113
-		}
114
-
115
-		try {
116
-			$ldapRecord = $this->getLDAPUserByLoginName($loginName);
117
-			$user = $this->access->userManager->get($ldapRecord['dn'][0]);
118
-			if($user instanceof OfflineUser) {
119
-				// this path is not really possible, however get() is documented
120
-				// to return User or OfflineUser so we are very defensive here.
121
-				$this->access->connection->writeToCache($cacheKey, false);
122
-				return false;
123
-			}
124
-			$username = $user->getUsername();
125
-			$this->access->connection->writeToCache($cacheKey, $username);
126
-			return $username;
127
-		} catch (NotOnLDAP $e) {
128
-			$this->access->connection->writeToCache($cacheKey, false);
129
-			return false;
130
-		}
131
-	}
50
+    /** @var \OCP\IConfig */
51
+    protected $ocConfig;
52
+
53
+    /** @var INotificationManager */
54
+    protected $notificationManager;
55
+
56
+    /** @var string */
57
+    protected $currentUserInDeletionProcess;
58
+
59
+    /**
60
+     * @param Access $access
61
+     * @param \OCP\IConfig $ocConfig
62
+     * @param \OCP\Notification\IManager $notificationManager
63
+     * @param IUserSession $userSession
64
+     */
65
+    public function __construct(Access $access, IConfig $ocConfig, INotificationManager $notificationManager, IUserSession $userSession) {
66
+        parent::__construct($access);
67
+        $this->ocConfig = $ocConfig;
68
+        $this->notificationManager = $notificationManager;
69
+        $this->registerHooks($userSession);
70
+    }
71
+
72
+    protected function registerHooks(IUserSession $userSession) {
73
+        $userSession->listen('\OC\User', 'preDelete', [$this, 'preDeleteUser']);
74
+        $userSession->listen('\OC\User', 'postDelete', [$this, 'postDeleteUser']);
75
+    }
76
+
77
+    public function preDeleteUser(IUser $user) {
78
+        $this->currentUserInDeletionProcess = $user->getUID();
79
+    }
80
+
81
+    public function postDeleteUser() {
82
+        $this->currentUserInDeletionProcess = null;
83
+    }
84
+
85
+    /**
86
+     * checks whether the user is allowed to change his avatar in Nextcloud
87
+     * @param string $uid the Nextcloud user name
88
+     * @return boolean either the user can or cannot
89
+     */
90
+    public function canChangeAvatar($uid) {
91
+        $user = $this->access->userManager->get($uid);
92
+        if(!$user instanceof User) {
93
+            return false;
94
+        }
95
+        if($user->getAvatarImage() === false) {
96
+            return true;
97
+        }
98
+
99
+        return false;
100
+    }
101
+
102
+    /**
103
+     * returns the username for the given login name, if available
104
+     *
105
+     * @param string $loginName
106
+     * @return string|false
107
+     */
108
+    public function loginName2UserName($loginName) {
109
+        $cacheKey = 'loginName2UserName-'.$loginName;
110
+        $username = $this->access->connection->getFromCache($cacheKey);
111
+        if(!is_null($username)) {
112
+            return $username;
113
+        }
114
+
115
+        try {
116
+            $ldapRecord = $this->getLDAPUserByLoginName($loginName);
117
+            $user = $this->access->userManager->get($ldapRecord['dn'][0]);
118
+            if($user instanceof OfflineUser) {
119
+                // this path is not really possible, however get() is documented
120
+                // to return User or OfflineUser so we are very defensive here.
121
+                $this->access->connection->writeToCache($cacheKey, false);
122
+                return false;
123
+            }
124
+            $username = $user->getUsername();
125
+            $this->access->connection->writeToCache($cacheKey, $username);
126
+            return $username;
127
+        } catch (NotOnLDAP $e) {
128
+            $this->access->connection->writeToCache($cacheKey, false);
129
+            return false;
130
+        }
131
+    }
132 132
 	
133
-	/**
134
-	 * returns the username for the given LDAP DN, if available
135
-	 *
136
-	 * @param string $dn
137
-	 * @return string|false with the username
138
-	 */
139
-	public function dn2UserName($dn) {
140
-		return $this->access->dn2username($dn);
141
-	}
142
-
143
-	/**
144
-	 * returns an LDAP record based on a given login name
145
-	 *
146
-	 * @param string $loginName
147
-	 * @return array
148
-	 * @throws NotOnLDAP
149
-	 */
150
-	public function getLDAPUserByLoginName($loginName) {
151
-		//find out dn of the user name
152
-		$attrs = $this->access->userManager->getAttributes();
153
-		$users = $this->access->fetchUsersByLoginName($loginName, $attrs);
154
-		if(count($users) < 1) {
155
-			throw new NotOnLDAP('No user available for the given login name on ' .
156
-				$this->access->connection->ldapHost . ':' . $this->access->connection->ldapPort);
157
-		}
158
-		return $users[0];
159
-	}
160
-
161
-	/**
162
-	 * Check if the password is correct without logging in the user
163
-	 *
164
-	 * @param string $uid The username
165
-	 * @param string $password The password
166
-	 * @return false|string
167
-	 */
168
-	public function checkPassword($uid, $password) {
169
-		try {
170
-			$ldapRecord = $this->getLDAPUserByLoginName($uid);
171
-		} catch(NotOnLDAP $e) {
172
-			if($this->ocConfig->getSystemValue('loglevel', Util::WARN) === Util::DEBUG) {
173
-				\OC::$server->getLogger()->logException($e, ['app' => 'user_ldap']);
174
-			}
175
-			return false;
176
-		}
177
-		$dn = $ldapRecord['dn'][0];
178
-		$user = $this->access->userManager->get($dn);
179
-
180
-		if(!$user instanceof User) {
181
-			Util::writeLog('user_ldap',
182
-				'LDAP Login: Could not get user object for DN ' . $dn .
183
-				'. Maybe the LDAP entry has no set display name attribute?',
184
-				Util::WARN);
185
-			return false;
186
-		}
187
-		if($user->getUsername() !== false) {
188
-			//are the credentials OK?
189
-			if(!$this->access->areCredentialsValid($dn, $password)) {
190
-				return false;
191
-			}
192
-
193
-			$this->access->cacheUserExists($user->getUsername());
194
-			$user->processAttributes($ldapRecord);
195
-			$user->markLogin();
196
-
197
-			return $user->getUsername();
198
-		}
199
-
200
-		return false;
201
-	}
202
-
203
-	/**
204
-	 * Set password
205
-	 * @param string $uid The username
206
-	 * @param string $password The new password
207
-	 * @return bool
208
-	 */
209
-	public function setPassword($uid, $password) {
210
-		$user = $this->access->userManager->get($uid);
211
-
212
-		if(!$user instanceof User) {
213
-			throw new \Exception('LDAP setPassword: Could not get user object for uid ' . $uid .
214
-				'. Maybe the LDAP entry has no set display name attribute?');
215
-		}
216
-		if($user->getUsername() !== false && $this->access->setPassword($user->getDN(), $password)) {
217
-			$ldapDefaultPPolicyDN = $this->access->connection->ldapDefaultPPolicyDN;
218
-			$turnOnPasswordChange = $this->access->connection->turnOnPasswordChange;
219
-			if (!empty($ldapDefaultPPolicyDN) && (intval($turnOnPasswordChange) === 1)) {
220
-				//remove last password expiry warning if any
221
-				$notification = $this->notificationManager->createNotification();
222
-				$notification->setApp('user_ldap')
223
-					->setUser($uid)
224
-					->setObject('pwd_exp_warn', $uid)
225
-				;
226
-				$this->notificationManager->markProcessed($notification);
227
-			}
228
-			return true;
229
-		}
230
-
231
-		return false;
232
-	}
233
-
234
-	/**
235
-	 * Get a list of all users
236
-	 *
237
-	 * @param string $search
238
-	 * @param integer $limit
239
-	 * @param integer $offset
240
-	 * @return string[] an array of all uids
241
-	 */
242
-	public function getUsers($search = '', $limit = 10, $offset = 0) {
243
-		$search = $this->access->escapeFilterPart($search, true);
244
-		$cachekey = 'getUsers-'.$search.'-'.$limit.'-'.$offset;
245
-
246
-		//check if users are cached, if so return
247
-		$ldap_users = $this->access->connection->getFromCache($cachekey);
248
-		if(!is_null($ldap_users)) {
249
-			return $ldap_users;
250
-		}
251
-
252
-		// if we'd pass -1 to LDAP search, we'd end up in a Protocol
253
-		// error. With a limit of 0, we get 0 results. So we pass null.
254
-		if($limit <= 0) {
255
-			$limit = null;
256
-		}
257
-		$filter = $this->access->combineFilterWithAnd(array(
258
-			$this->access->connection->ldapUserFilter,
259
-			$this->access->connection->ldapUserDisplayName . '=*',
260
-			$this->access->getFilterPartForUserSearch($search)
261
-		));
262
-
263
-		Util::writeLog('user_ldap',
264
-			'getUsers: Options: search '.$search.' limit '.$limit.' offset '.$offset.' Filter: '.$filter,
265
-			Util::DEBUG);
266
-		//do the search and translate results to Nextcloud names
267
-		$ldap_users = $this->access->fetchListOfUsers(
268
-			$filter,
269
-			$this->access->userManager->getAttributes(true),
270
-			$limit, $offset);
271
-		$ldap_users = $this->access->nextcloudUserNames($ldap_users);
272
-		Util::writeLog('user_ldap', 'getUsers: '.count($ldap_users). ' Users found', Util::DEBUG);
273
-
274
-		$this->access->connection->writeToCache($cachekey, $ldap_users);
275
-		return $ldap_users;
276
-	}
277
-
278
-	/**
279
-	 * checks whether a user is still available on LDAP
280
-	 *
281
-	 * @param string|\OCA\User_LDAP\User\User $user either the Nextcloud user
282
-	 * name or an instance of that user
283
-	 * @return bool
284
-	 * @throws \Exception
285
-	 * @throws \OC\ServerNotAvailableException
286
-	 */
287
-	public function userExistsOnLDAP($user) {
288
-		if(is_string($user)) {
289
-			$user = $this->access->userManager->get($user);
290
-		}
291
-		if(is_null($user)) {
292
-			return false;
293
-		}
294
-
295
-		$dn = $user->getDN();
296
-		//check if user really still exists by reading its entry
297
-		if(!is_array($this->access->readAttribute($dn, '', $this->access->connection->ldapUserFilter))) {
298
-			$lcr = $this->access->connection->getConnectionResource();
299
-			if(is_null($lcr)) {
300
-				throw new \Exception('No LDAP Connection to server ' . $this->access->connection->ldapHost);
301
-			}
302
-
303
-			try {
304
-				$uuid = $this->access->getUserMapper()->getUUIDByDN($dn);
305
-				if(!$uuid) {
306
-					return false;
307
-				}
308
-				$newDn = $this->access->getUserDnByUuid($uuid);
309
-				//check if renamed user is still valid by reapplying the ldap filter
310
-				if(!is_array($this->access->readAttribute($newDn, '', $this->access->connection->ldapUserFilter))) {
311
-					return false;
312
-				}
313
-				$this->access->getUserMapper()->setDNbyUUID($newDn, $uuid);
314
-				return true;
315
-			} catch (\Exception $e) {
316
-				return false;
317
-			}
318
-		}
319
-
320
-		if($user instanceof OfflineUser) {
321
-			$user->unmark();
322
-		}
323
-
324
-		return true;
325
-	}
326
-
327
-	/**
328
-	 * check if a user exists
329
-	 * @param string $uid the username
330
-	 * @return boolean
331
-	 * @throws \Exception when connection could not be established
332
-	 */
333
-	public function userExists($uid) {
334
-		$userExists = $this->access->connection->getFromCache('userExists'.$uid);
335
-		if(!is_null($userExists)) {
336
-			return (bool)$userExists;
337
-		}
338
-		//getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking.
339
-		$user = $this->access->userManager->get($uid);
340
-
341
-		if(is_null($user)) {
342
-			Util::writeLog('user_ldap', 'No DN found for '.$uid.' on '.
343
-				$this->access->connection->ldapHost, Util::DEBUG);
344
-			$this->access->connection->writeToCache('userExists'.$uid, false);
345
-			return false;
346
-		} else if($user instanceof OfflineUser) {
347
-			//express check for users marked as deleted. Returning true is
348
-			//necessary for cleanup
349
-			return true;
350
-		}
351
-
352
-		$result = $this->userExistsOnLDAP($user);
353
-		$this->access->connection->writeToCache('userExists'.$uid, $result);
354
-		if($result === true) {
355
-			$user->update();
356
-		}
357
-		return $result;
358
-	}
359
-
360
-	/**
361
-	* returns whether a user was deleted in LDAP
362
-	*
363
-	* @param string $uid The username of the user to delete
364
-	* @return bool
365
-	*/
366
-	public function deleteUser($uid) {
367
-		$marked = $this->ocConfig->getUserValue($uid, 'user_ldap', 'isDeleted', 0);
368
-		if(intval($marked) === 0) {
369
-			\OC::$server->getLogger()->notice(
370
-				'User '.$uid . ' is not marked as deleted, not cleaning up.',
371
-				array('app' => 'user_ldap'));
372
-			return false;
373
-		}
374
-		\OC::$server->getLogger()->info('Cleaning up after user ' . $uid,
375
-			array('app' => 'user_ldap'));
376
-
377
-		$this->access->getUserMapper()->unmap($uid);
378
-		$this->access->userManager->invalidate($uid);
379
-		return true;
380
-	}
381
-
382
-	/**
383
-	 * get the user's home directory
384
-	 *
385
-	 * @param string $uid the username
386
-	 * @return bool|string
387
-	 * @throws NoUserException
388
-	 * @throws \Exception
389
-	 */
390
-	public function getHome($uid) {
391
-		// user Exists check required as it is not done in user proxy!
392
-		if(!$this->userExists($uid)) {
393
-			return false;
394
-		}
395
-
396
-		$cacheKey = 'getHome'.$uid;
397
-		$path = $this->access->connection->getFromCache($cacheKey);
398
-		if(!is_null($path)) {
399
-			return $path;
400
-		}
401
-
402
-		// early return path if it is a deleted user
403
-		$user = $this->access->userManager->get($uid);
404
-		if($user instanceof OfflineUser) {
405
-			if($this->currentUserInDeletionProcess !== null
406
-				&& $this->currentUserInDeletionProcess === $user->getOCName()
407
-			) {
408
-				return $user->getHomePath();
409
-			} else {
410
-				throw new NoUserException($uid . ' is not a valid user anymore');
411
-			}
412
-		} else if ($user === null) {
413
-			throw new NoUserException($uid . ' is not a valid user anymore');
414
-		}
415
-
416
-		$path = $user->getHomePath();
417
-		$this->access->cacheUserHome($uid, $path);
418
-
419
-		return $path;
420
-	}
421
-
422
-	/**
423
-	 * get display name of the user
424
-	 * @param string $uid user ID of the user
425
-	 * @return string|false display name
426
-	 */
427
-	public function getDisplayName($uid) {
428
-		if(!$this->userExists($uid)) {
429
-			return false;
430
-		}
431
-
432
-		$cacheKey = 'getDisplayName'.$uid;
433
-		if(!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) {
434
-			return $displayName;
435
-		}
436
-
437
-		//Check whether the display name is configured to have a 2nd feature
438
-		$additionalAttribute = $this->access->connection->ldapUserDisplayName2;
439
-		$displayName2 = '';
440
-		if ($additionalAttribute !== '') {
441
-			$displayName2 = $this->access->readAttribute(
442
-				$this->access->username2dn($uid),
443
-				$additionalAttribute);
444
-		}
445
-
446
-		$displayName = $this->access->readAttribute(
447
-			$this->access->username2dn($uid),
448
-			$this->access->connection->ldapUserDisplayName);
449
-
450
-		if($displayName && (count($displayName) > 0)) {
451
-			$displayName = $displayName[0];
452
-
453
-			if (is_array($displayName2)){
454
-				$displayName2 = count($displayName2) > 0 ? $displayName2[0] : '';
455
-			}
456
-
457
-			$user = $this->access->userManager->get($uid);
458
-			if ($user instanceof User) {
459
-				$displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
460
-				$this->access->connection->writeToCache($cacheKey, $displayName);
461
-			}
462
-			if ($user instanceof OfflineUser) {
463
-				/** @var OfflineUser $user*/
464
-				$displayName = $user->getDisplayName();
465
-			}
466
-			return $displayName;
467
-		}
468
-
469
-		return null;
470
-	}
471
-
472
-	/**
473
-	 * Get a list of all display names
474
-	 *
475
-	 * @param string $search
476
-	 * @param string|null $limit
477
-	 * @param string|null $offset
478
-	 * @return array an array of all displayNames (value) and the corresponding uids (key)
479
-	 */
480
-	public function getDisplayNames($search = '', $limit = null, $offset = null) {
481
-		$cacheKey = 'getDisplayNames-'.$search.'-'.$limit.'-'.$offset;
482
-		if(!is_null($displayNames = $this->access->connection->getFromCache($cacheKey))) {
483
-			return $displayNames;
484
-		}
485
-
486
-		$displayNames = array();
487
-		$users = $this->getUsers($search, $limit, $offset);
488
-		foreach ($users as $user) {
489
-			$displayNames[$user] = $this->getDisplayName($user);
490
-		}
491
-		$this->access->connection->writeToCache($cacheKey, $displayNames);
492
-		return $displayNames;
493
-	}
494
-
495
-	/**
496
-	* Check if backend implements actions
497
-	* @param int $actions bitwise-or'ed actions
498
-	* @return boolean
499
-	*
500
-	* Returns the supported actions as int to be
501
-	* compared with \OC\User\Backend::CREATE_USER etc.
502
-	*/
503
-	public function implementsActions($actions) {
504
-		return (bool)((Backend::CHECK_PASSWORD
505
-			| Backend::GET_HOME
506
-			| Backend::GET_DISPLAYNAME
507
-			| Backend::PROVIDE_AVATAR
508
-			| Backend::COUNT_USERS
509
-			| ((intval($this->access->connection->turnOnPasswordChange) === 1)?(Backend::SET_PASSWORD):0))
510
-			& $actions);
511
-	}
512
-
513
-	/**
514
-	 * @return bool
515
-	 */
516
-	public function hasUserListings() {
517
-		return true;
518
-	}
519
-
520
-	/**
521
-	 * counts the users in LDAP
522
-	 *
523
-	 * @return int|bool
524
-	 */
525
-	public function countUsers() {
526
-		$filter = $this->access->getFilterForUserCount();
527
-		$cacheKey = 'countUsers-'.$filter;
528
-		if(!is_null($entries = $this->access->connection->getFromCache($cacheKey))) {
529
-			return $entries;
530
-		}
531
-		$entries = $this->access->countUsers($filter);
532
-		$this->access->connection->writeToCache($cacheKey, $entries);
533
-		return $entries;
534
-	}
535
-
536
-	/**
537
-	 * Backend name to be shown in user management
538
-	 * @return string the name of the backend to be shown
539
-	 */
540
-	public function getBackendName(){
541
-		return 'LDAP';
542
-	}
133
+    /**
134
+     * returns the username for the given LDAP DN, if available
135
+     *
136
+     * @param string $dn
137
+     * @return string|false with the username
138
+     */
139
+    public function dn2UserName($dn) {
140
+        return $this->access->dn2username($dn);
141
+    }
142
+
143
+    /**
144
+     * returns an LDAP record based on a given login name
145
+     *
146
+     * @param string $loginName
147
+     * @return array
148
+     * @throws NotOnLDAP
149
+     */
150
+    public function getLDAPUserByLoginName($loginName) {
151
+        //find out dn of the user name
152
+        $attrs = $this->access->userManager->getAttributes();
153
+        $users = $this->access->fetchUsersByLoginName($loginName, $attrs);
154
+        if(count($users) < 1) {
155
+            throw new NotOnLDAP('No user available for the given login name on ' .
156
+                $this->access->connection->ldapHost . ':' . $this->access->connection->ldapPort);
157
+        }
158
+        return $users[0];
159
+    }
160
+
161
+    /**
162
+     * Check if the password is correct without logging in the user
163
+     *
164
+     * @param string $uid The username
165
+     * @param string $password The password
166
+     * @return false|string
167
+     */
168
+    public function checkPassword($uid, $password) {
169
+        try {
170
+            $ldapRecord = $this->getLDAPUserByLoginName($uid);
171
+        } catch(NotOnLDAP $e) {
172
+            if($this->ocConfig->getSystemValue('loglevel', Util::WARN) === Util::DEBUG) {
173
+                \OC::$server->getLogger()->logException($e, ['app' => 'user_ldap']);
174
+            }
175
+            return false;
176
+        }
177
+        $dn = $ldapRecord['dn'][0];
178
+        $user = $this->access->userManager->get($dn);
179
+
180
+        if(!$user instanceof User) {
181
+            Util::writeLog('user_ldap',
182
+                'LDAP Login: Could not get user object for DN ' . $dn .
183
+                '. Maybe the LDAP entry has no set display name attribute?',
184
+                Util::WARN);
185
+            return false;
186
+        }
187
+        if($user->getUsername() !== false) {
188
+            //are the credentials OK?
189
+            if(!$this->access->areCredentialsValid($dn, $password)) {
190
+                return false;
191
+            }
192
+
193
+            $this->access->cacheUserExists($user->getUsername());
194
+            $user->processAttributes($ldapRecord);
195
+            $user->markLogin();
196
+
197
+            return $user->getUsername();
198
+        }
199
+
200
+        return false;
201
+    }
202
+
203
+    /**
204
+     * Set password
205
+     * @param string $uid The username
206
+     * @param string $password The new password
207
+     * @return bool
208
+     */
209
+    public function setPassword($uid, $password) {
210
+        $user = $this->access->userManager->get($uid);
211
+
212
+        if(!$user instanceof User) {
213
+            throw new \Exception('LDAP setPassword: Could not get user object for uid ' . $uid .
214
+                '. Maybe the LDAP entry has no set display name attribute?');
215
+        }
216
+        if($user->getUsername() !== false && $this->access->setPassword($user->getDN(), $password)) {
217
+            $ldapDefaultPPolicyDN = $this->access->connection->ldapDefaultPPolicyDN;
218
+            $turnOnPasswordChange = $this->access->connection->turnOnPasswordChange;
219
+            if (!empty($ldapDefaultPPolicyDN) && (intval($turnOnPasswordChange) === 1)) {
220
+                //remove last password expiry warning if any
221
+                $notification = $this->notificationManager->createNotification();
222
+                $notification->setApp('user_ldap')
223
+                    ->setUser($uid)
224
+                    ->setObject('pwd_exp_warn', $uid)
225
+                ;
226
+                $this->notificationManager->markProcessed($notification);
227
+            }
228
+            return true;
229
+        }
230
+
231
+        return false;
232
+    }
233
+
234
+    /**
235
+     * Get a list of all users
236
+     *
237
+     * @param string $search
238
+     * @param integer $limit
239
+     * @param integer $offset
240
+     * @return string[] an array of all uids
241
+     */
242
+    public function getUsers($search = '', $limit = 10, $offset = 0) {
243
+        $search = $this->access->escapeFilterPart($search, true);
244
+        $cachekey = 'getUsers-'.$search.'-'.$limit.'-'.$offset;
245
+
246
+        //check if users are cached, if so return
247
+        $ldap_users = $this->access->connection->getFromCache($cachekey);
248
+        if(!is_null($ldap_users)) {
249
+            return $ldap_users;
250
+        }
251
+
252
+        // if we'd pass -1 to LDAP search, we'd end up in a Protocol
253
+        // error. With a limit of 0, we get 0 results. So we pass null.
254
+        if($limit <= 0) {
255
+            $limit = null;
256
+        }
257
+        $filter = $this->access->combineFilterWithAnd(array(
258
+            $this->access->connection->ldapUserFilter,
259
+            $this->access->connection->ldapUserDisplayName . '=*',
260
+            $this->access->getFilterPartForUserSearch($search)
261
+        ));
262
+
263
+        Util::writeLog('user_ldap',
264
+            'getUsers: Options: search '.$search.' limit '.$limit.' offset '.$offset.' Filter: '.$filter,
265
+            Util::DEBUG);
266
+        //do the search and translate results to Nextcloud names
267
+        $ldap_users = $this->access->fetchListOfUsers(
268
+            $filter,
269
+            $this->access->userManager->getAttributes(true),
270
+            $limit, $offset);
271
+        $ldap_users = $this->access->nextcloudUserNames($ldap_users);
272
+        Util::writeLog('user_ldap', 'getUsers: '.count($ldap_users). ' Users found', Util::DEBUG);
273
+
274
+        $this->access->connection->writeToCache($cachekey, $ldap_users);
275
+        return $ldap_users;
276
+    }
277
+
278
+    /**
279
+     * checks whether a user is still available on LDAP
280
+     *
281
+     * @param string|\OCA\User_LDAP\User\User $user either the Nextcloud user
282
+     * name or an instance of that user
283
+     * @return bool
284
+     * @throws \Exception
285
+     * @throws \OC\ServerNotAvailableException
286
+     */
287
+    public function userExistsOnLDAP($user) {
288
+        if(is_string($user)) {
289
+            $user = $this->access->userManager->get($user);
290
+        }
291
+        if(is_null($user)) {
292
+            return false;
293
+        }
294
+
295
+        $dn = $user->getDN();
296
+        //check if user really still exists by reading its entry
297
+        if(!is_array($this->access->readAttribute($dn, '', $this->access->connection->ldapUserFilter))) {
298
+            $lcr = $this->access->connection->getConnectionResource();
299
+            if(is_null($lcr)) {
300
+                throw new \Exception('No LDAP Connection to server ' . $this->access->connection->ldapHost);
301
+            }
302
+
303
+            try {
304
+                $uuid = $this->access->getUserMapper()->getUUIDByDN($dn);
305
+                if(!$uuid) {
306
+                    return false;
307
+                }
308
+                $newDn = $this->access->getUserDnByUuid($uuid);
309
+                //check if renamed user is still valid by reapplying the ldap filter
310
+                if(!is_array($this->access->readAttribute($newDn, '', $this->access->connection->ldapUserFilter))) {
311
+                    return false;
312
+                }
313
+                $this->access->getUserMapper()->setDNbyUUID($newDn, $uuid);
314
+                return true;
315
+            } catch (\Exception $e) {
316
+                return false;
317
+            }
318
+        }
319
+
320
+        if($user instanceof OfflineUser) {
321
+            $user->unmark();
322
+        }
323
+
324
+        return true;
325
+    }
326
+
327
+    /**
328
+     * check if a user exists
329
+     * @param string $uid the username
330
+     * @return boolean
331
+     * @throws \Exception when connection could not be established
332
+     */
333
+    public function userExists($uid) {
334
+        $userExists = $this->access->connection->getFromCache('userExists'.$uid);
335
+        if(!is_null($userExists)) {
336
+            return (bool)$userExists;
337
+        }
338
+        //getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking.
339
+        $user = $this->access->userManager->get($uid);
340
+
341
+        if(is_null($user)) {
342
+            Util::writeLog('user_ldap', 'No DN found for '.$uid.' on '.
343
+                $this->access->connection->ldapHost, Util::DEBUG);
344
+            $this->access->connection->writeToCache('userExists'.$uid, false);
345
+            return false;
346
+        } else if($user instanceof OfflineUser) {
347
+            //express check for users marked as deleted. Returning true is
348
+            //necessary for cleanup
349
+            return true;
350
+        }
351
+
352
+        $result = $this->userExistsOnLDAP($user);
353
+        $this->access->connection->writeToCache('userExists'.$uid, $result);
354
+        if($result === true) {
355
+            $user->update();
356
+        }
357
+        return $result;
358
+    }
359
+
360
+    /**
361
+     * returns whether a user was deleted in LDAP
362
+     *
363
+     * @param string $uid The username of the user to delete
364
+     * @return bool
365
+     */
366
+    public function deleteUser($uid) {
367
+        $marked = $this->ocConfig->getUserValue($uid, 'user_ldap', 'isDeleted', 0);
368
+        if(intval($marked) === 0) {
369
+            \OC::$server->getLogger()->notice(
370
+                'User '.$uid . ' is not marked as deleted, not cleaning up.',
371
+                array('app' => 'user_ldap'));
372
+            return false;
373
+        }
374
+        \OC::$server->getLogger()->info('Cleaning up after user ' . $uid,
375
+            array('app' => 'user_ldap'));
376
+
377
+        $this->access->getUserMapper()->unmap($uid);
378
+        $this->access->userManager->invalidate($uid);
379
+        return true;
380
+    }
381
+
382
+    /**
383
+     * get the user's home directory
384
+     *
385
+     * @param string $uid the username
386
+     * @return bool|string
387
+     * @throws NoUserException
388
+     * @throws \Exception
389
+     */
390
+    public function getHome($uid) {
391
+        // user Exists check required as it is not done in user proxy!
392
+        if(!$this->userExists($uid)) {
393
+            return false;
394
+        }
395
+
396
+        $cacheKey = 'getHome'.$uid;
397
+        $path = $this->access->connection->getFromCache($cacheKey);
398
+        if(!is_null($path)) {
399
+            return $path;
400
+        }
401
+
402
+        // early return path if it is a deleted user
403
+        $user = $this->access->userManager->get($uid);
404
+        if($user instanceof OfflineUser) {
405
+            if($this->currentUserInDeletionProcess !== null
406
+                && $this->currentUserInDeletionProcess === $user->getOCName()
407
+            ) {
408
+                return $user->getHomePath();
409
+            } else {
410
+                throw new NoUserException($uid . ' is not a valid user anymore');
411
+            }
412
+        } else if ($user === null) {
413
+            throw new NoUserException($uid . ' is not a valid user anymore');
414
+        }
415
+
416
+        $path = $user->getHomePath();
417
+        $this->access->cacheUserHome($uid, $path);
418
+
419
+        return $path;
420
+    }
421
+
422
+    /**
423
+     * get display name of the user
424
+     * @param string $uid user ID of the user
425
+     * @return string|false display name
426
+     */
427
+    public function getDisplayName($uid) {
428
+        if(!$this->userExists($uid)) {
429
+            return false;
430
+        }
431
+
432
+        $cacheKey = 'getDisplayName'.$uid;
433
+        if(!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) {
434
+            return $displayName;
435
+        }
436
+
437
+        //Check whether the display name is configured to have a 2nd feature
438
+        $additionalAttribute = $this->access->connection->ldapUserDisplayName2;
439
+        $displayName2 = '';
440
+        if ($additionalAttribute !== '') {
441
+            $displayName2 = $this->access->readAttribute(
442
+                $this->access->username2dn($uid),
443
+                $additionalAttribute);
444
+        }
445
+
446
+        $displayName = $this->access->readAttribute(
447
+            $this->access->username2dn($uid),
448
+            $this->access->connection->ldapUserDisplayName);
449
+
450
+        if($displayName && (count($displayName) > 0)) {
451
+            $displayName = $displayName[0];
452
+
453
+            if (is_array($displayName2)){
454
+                $displayName2 = count($displayName2) > 0 ? $displayName2[0] : '';
455
+            }
456
+
457
+            $user = $this->access->userManager->get($uid);
458
+            if ($user instanceof User) {
459
+                $displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
460
+                $this->access->connection->writeToCache($cacheKey, $displayName);
461
+            }
462
+            if ($user instanceof OfflineUser) {
463
+                /** @var OfflineUser $user*/
464
+                $displayName = $user->getDisplayName();
465
+            }
466
+            return $displayName;
467
+        }
468
+
469
+        return null;
470
+    }
471
+
472
+    /**
473
+     * Get a list of all display names
474
+     *
475
+     * @param string $search
476
+     * @param string|null $limit
477
+     * @param string|null $offset
478
+     * @return array an array of all displayNames (value) and the corresponding uids (key)
479
+     */
480
+    public function getDisplayNames($search = '', $limit = null, $offset = null) {
481
+        $cacheKey = 'getDisplayNames-'.$search.'-'.$limit.'-'.$offset;
482
+        if(!is_null($displayNames = $this->access->connection->getFromCache($cacheKey))) {
483
+            return $displayNames;
484
+        }
485
+
486
+        $displayNames = array();
487
+        $users = $this->getUsers($search, $limit, $offset);
488
+        foreach ($users as $user) {
489
+            $displayNames[$user] = $this->getDisplayName($user);
490
+        }
491
+        $this->access->connection->writeToCache($cacheKey, $displayNames);
492
+        return $displayNames;
493
+    }
494
+
495
+    /**
496
+     * Check if backend implements actions
497
+     * @param int $actions bitwise-or'ed actions
498
+     * @return boolean
499
+     *
500
+     * Returns the supported actions as int to be
501
+     * compared with \OC\User\Backend::CREATE_USER etc.
502
+     */
503
+    public function implementsActions($actions) {
504
+        return (bool)((Backend::CHECK_PASSWORD
505
+            | Backend::GET_HOME
506
+            | Backend::GET_DISPLAYNAME
507
+            | Backend::PROVIDE_AVATAR
508
+            | Backend::COUNT_USERS
509
+            | ((intval($this->access->connection->turnOnPasswordChange) === 1)?(Backend::SET_PASSWORD):0))
510
+            & $actions);
511
+    }
512
+
513
+    /**
514
+     * @return bool
515
+     */
516
+    public function hasUserListings() {
517
+        return true;
518
+    }
519
+
520
+    /**
521
+     * counts the users in LDAP
522
+     *
523
+     * @return int|bool
524
+     */
525
+    public function countUsers() {
526
+        $filter = $this->access->getFilterForUserCount();
527
+        $cacheKey = 'countUsers-'.$filter;
528
+        if(!is_null($entries = $this->access->connection->getFromCache($cacheKey))) {
529
+            return $entries;
530
+        }
531
+        $entries = $this->access->countUsers($filter);
532
+        $this->access->connection->writeToCache($cacheKey, $entries);
533
+        return $entries;
534
+    }
535
+
536
+    /**
537
+     * Backend name to be shown in user management
538
+     * @return string the name of the backend to be shown
539
+     */
540
+    public function getBackendName(){
541
+        return 'LDAP';
542
+    }
543 543
 	
544
-	/**
545
-	 * Return access for LDAP interaction.
546
-	 * @param string $uid
547
-	 * @return Access instance of Access for LDAP interaction
548
-	 */
549
-	public function getLDAPAccess($uid) {
550
-		return $this->access;
551
-	}
544
+    /**
545
+     * Return access for LDAP interaction.
546
+     * @param string $uid
547
+     * @return Access instance of Access for LDAP interaction
548
+     */
549
+    public function getLDAPAccess($uid) {
550
+        return $this->access;
551
+    }
552 552
 	
553
-	/**
554
-	 * Return LDAP connection resource from a cloned connection.
555
-	 * The cloned connection needs to be closed manually.
556
-	 * of the current access.
557
-	 * @param string $uid
558
-	 * @return resource of the LDAP connection
559
-	 */
560
-	public function getNewLDAPConnection($uid) {
561
-		$connection = clone $this->access->getConnection();
562
-		return $connection->getConnectionResource();
563
-	}
553
+    /**
554
+     * Return LDAP connection resource from a cloned connection.
555
+     * The cloned connection needs to be closed manually.
556
+     * of the current access.
557
+     * @param string $uid
558
+     * @return resource of the LDAP connection
559
+     */
560
+    public function getNewLDAPConnection($uid) {
561
+        $connection = clone $this->access->getConnection();
562
+        return $connection->getConnectionResource();
563
+    }
564 564
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/User/Manager.php 1 patch
Indentation   +193 added lines, -193 removed lines patch added patch discarded remove patch
@@ -44,223 +44,223 @@
 block discarded – undo
44 44
  * cache
45 45
  */
46 46
 class Manager {
47
-	/** @var IUserTools */
48
-	protected $access;
47
+    /** @var IUserTools */
48
+    protected $access;
49 49
 
50
-	/** @var IConfig */
51
-	protected $ocConfig;
50
+    /** @var IConfig */
51
+    protected $ocConfig;
52 52
 
53
-	/** @var IDBConnection */
54
-	protected $db;
53
+    /** @var IDBConnection */
54
+    protected $db;
55 55
 
56
-	/** @var IUserManager */
57
-	protected $userManager;
56
+    /** @var IUserManager */
57
+    protected $userManager;
58 58
 
59
-	/** @var INotificationManager */
60
-	protected $notificationManager;
59
+    /** @var INotificationManager */
60
+    protected $notificationManager;
61 61
 
62
-	/** @var FilesystemHelper */
63
-	protected $ocFilesystem;
62
+    /** @var FilesystemHelper */
63
+    protected $ocFilesystem;
64 64
 
65
-	/** @var LogWrapper */
66
-	protected $ocLog;
65
+    /** @var LogWrapper */
66
+    protected $ocLog;
67 67
 
68
-	/** @var Image */
69
-	protected $image;
68
+    /** @var Image */
69
+    protected $image;
70 70
 
71
-	/** @param \OCP\IAvatarManager */
72
-	protected $avatarManager;
71
+    /** @param \OCP\IAvatarManager */
72
+    protected $avatarManager;
73 73
 
74
-	/**
75
-	 * @var CappedMemoryCache $usersByDN
76
-	 */
77
-	protected $usersByDN;
78
-	/**
79
-	 * @var CappedMemoryCache $usersByUid
80
-	 */
81
-	protected $usersByUid;
74
+    /**
75
+     * @var CappedMemoryCache $usersByDN
76
+     */
77
+    protected $usersByDN;
78
+    /**
79
+     * @var CappedMemoryCache $usersByUid
80
+     */
81
+    protected $usersByUid;
82 82
 
83
-	/**
84
-	 * @param IConfig $ocConfig
85
-	 * @param \OCA\User_LDAP\FilesystemHelper $ocFilesystem object that
86
-	 * gives access to necessary functions from the OC filesystem
87
-	 * @param  \OCA\User_LDAP\LogWrapper $ocLog
88
-	 * @param IAvatarManager $avatarManager
89
-	 * @param Image $image an empty image instance
90
-	 * @param IDBConnection $db
91
-	 * @throws \Exception when the methods mentioned above do not exist
92
-	 */
93
-	public function __construct(IConfig $ocConfig,
94
-								FilesystemHelper $ocFilesystem, LogWrapper $ocLog,
95
-								IAvatarManager $avatarManager, Image $image,
96
-								IDBConnection $db, IUserManager $userManager,
97
-								INotificationManager $notificationManager) {
83
+    /**
84
+     * @param IConfig $ocConfig
85
+     * @param \OCA\User_LDAP\FilesystemHelper $ocFilesystem object that
86
+     * gives access to necessary functions from the OC filesystem
87
+     * @param  \OCA\User_LDAP\LogWrapper $ocLog
88
+     * @param IAvatarManager $avatarManager
89
+     * @param Image $image an empty image instance
90
+     * @param IDBConnection $db
91
+     * @throws \Exception when the methods mentioned above do not exist
92
+     */
93
+    public function __construct(IConfig $ocConfig,
94
+                                FilesystemHelper $ocFilesystem, LogWrapper $ocLog,
95
+                                IAvatarManager $avatarManager, Image $image,
96
+                                IDBConnection $db, IUserManager $userManager,
97
+                                INotificationManager $notificationManager) {
98 98
 
99
-		$this->ocConfig            = $ocConfig;
100
-		$this->ocFilesystem        = $ocFilesystem;
101
-		$this->ocLog               = $ocLog;
102
-		$this->avatarManager       = $avatarManager;
103
-		$this->image               = $image;
104
-		$this->db                  = $db;
105
-		$this->userManager         = $userManager;
106
-		$this->notificationManager = $notificationManager;
107
-		$this->usersByDN           = new CappedMemoryCache();
108
-		$this->usersByUid          = new CappedMemoryCache();
109
-	}
99
+        $this->ocConfig            = $ocConfig;
100
+        $this->ocFilesystem        = $ocFilesystem;
101
+        $this->ocLog               = $ocLog;
102
+        $this->avatarManager       = $avatarManager;
103
+        $this->image               = $image;
104
+        $this->db                  = $db;
105
+        $this->userManager         = $userManager;
106
+        $this->notificationManager = $notificationManager;
107
+        $this->usersByDN           = new CappedMemoryCache();
108
+        $this->usersByUid          = new CappedMemoryCache();
109
+    }
110 110
 
111
-	/**
112
-	 * @brief binds manager to an instance of IUserTools (implemented by
113
-	 * Access). It needs to be assigned first before the manager can be used.
114
-	 * @param IUserTools
115
-	 */
116
-	public function setLdapAccess(IUserTools $access) {
117
-		$this->access = $access;
118
-	}
111
+    /**
112
+     * @brief binds manager to an instance of IUserTools (implemented by
113
+     * Access). It needs to be assigned first before the manager can be used.
114
+     * @param IUserTools
115
+     */
116
+    public function setLdapAccess(IUserTools $access) {
117
+        $this->access = $access;
118
+    }
119 119
 
120
-	/**
121
-	 * @brief creates an instance of User and caches (just runtime) it in the
122
-	 * property array
123
-	 * @param string $dn the DN of the user
124
-	 * @param string $uid the internal (owncloud) username
125
-	 * @return \OCA\User_LDAP\User\User
126
-	 */
127
-	private function createAndCache($dn, $uid) {
128
-		$this->checkAccess();
129
-		$user = new User($uid, $dn, $this->access, $this->ocConfig,
130
-			$this->ocFilesystem, clone $this->image, $this->ocLog,
131
-			$this->avatarManager, $this->userManager, 
132
-			$this->notificationManager);
133
-		$this->usersByDN[$dn]   = $user;
134
-		$this->usersByUid[$uid] = $user;
135
-		return $user;
136
-	}
120
+    /**
121
+     * @brief creates an instance of User and caches (just runtime) it in the
122
+     * property array
123
+     * @param string $dn the DN of the user
124
+     * @param string $uid the internal (owncloud) username
125
+     * @return \OCA\User_LDAP\User\User
126
+     */
127
+    private function createAndCache($dn, $uid) {
128
+        $this->checkAccess();
129
+        $user = new User($uid, $dn, $this->access, $this->ocConfig,
130
+            $this->ocFilesystem, clone $this->image, $this->ocLog,
131
+            $this->avatarManager, $this->userManager, 
132
+            $this->notificationManager);
133
+        $this->usersByDN[$dn]   = $user;
134
+        $this->usersByUid[$uid] = $user;
135
+        return $user;
136
+    }
137 137
 
138
-	/**
139
-	 * removes a user entry from the cache
140
-	 * @param $uid
141
-	 */
142
-	public function invalidate($uid) {
143
-		if(!isset($this->usersByUid[$uid])) {
144
-			return;
145
-		}
146
-		$dn = $this->usersByUid[$uid]->getDN();
147
-		unset($this->usersByUid[$uid]);
148
-		unset($this->usersByDN[$dn]);
149
-	}
138
+    /**
139
+     * removes a user entry from the cache
140
+     * @param $uid
141
+     */
142
+    public function invalidate($uid) {
143
+        if(!isset($this->usersByUid[$uid])) {
144
+            return;
145
+        }
146
+        $dn = $this->usersByUid[$uid]->getDN();
147
+        unset($this->usersByUid[$uid]);
148
+        unset($this->usersByDN[$dn]);
149
+    }
150 150
 
151
-	/**
152
-	 * @brief checks whether the Access instance has been set
153
-	 * @throws \Exception if Access has not been set
154
-	 * @return null
155
-	 */
156
-	private function checkAccess() {
157
-		if(is_null($this->access)) {
158
-			throw new \Exception('LDAP Access instance must be set first');
159
-		}
160
-	}
151
+    /**
152
+     * @brief checks whether the Access instance has been set
153
+     * @throws \Exception if Access has not been set
154
+     * @return null
155
+     */
156
+    private function checkAccess() {
157
+        if(is_null($this->access)) {
158
+            throw new \Exception('LDAP Access instance must be set first');
159
+        }
160
+    }
161 161
 
162
-	/**
163
-	 * returns a list of attributes that will be processed further, e.g. quota,
164
-	 * email, displayname, or others.
165
-	 * @param bool $minimal - optional, set to true to skip attributes with big
166
-	 * payload
167
-	 * @return string[]
168
-	 */
169
-	public function getAttributes($minimal = false) {
170
-		$attributes = array_merge(Access::UUID_ATTRIBUTES, ['dn', 'uid', 'samaccountname', 'memberof']);
171
-		$possible = array(
172
-			$this->access->getConnection()->ldapExpertUUIDUserAttr,
173
-			$this->access->getConnection()->ldapQuotaAttribute,
174
-			$this->access->getConnection()->ldapEmailAttribute,
175
-			$this->access->getConnection()->ldapUserDisplayName,
176
-			$this->access->getConnection()->ldapUserDisplayName2,
177
-		);
178
-		foreach($possible as $attr) {
179
-			if(!is_null($attr)) {
180
-				$attributes[] = $attr;
181
-			}
182
-		}
162
+    /**
163
+     * returns a list of attributes that will be processed further, e.g. quota,
164
+     * email, displayname, or others.
165
+     * @param bool $minimal - optional, set to true to skip attributes with big
166
+     * payload
167
+     * @return string[]
168
+     */
169
+    public function getAttributes($minimal = false) {
170
+        $attributes = array_merge(Access::UUID_ATTRIBUTES, ['dn', 'uid', 'samaccountname', 'memberof']);
171
+        $possible = array(
172
+            $this->access->getConnection()->ldapExpertUUIDUserAttr,
173
+            $this->access->getConnection()->ldapQuotaAttribute,
174
+            $this->access->getConnection()->ldapEmailAttribute,
175
+            $this->access->getConnection()->ldapUserDisplayName,
176
+            $this->access->getConnection()->ldapUserDisplayName2,
177
+        );
178
+        foreach($possible as $attr) {
179
+            if(!is_null($attr)) {
180
+                $attributes[] = $attr;
181
+            }
182
+        }
183 183
 
184
-		$homeRule = $this->access->getConnection()->homeFolderNamingRule;
185
-		if(strpos($homeRule, 'attr:') === 0) {
186
-			$attributes[] = substr($homeRule, strlen('attr:'));
187
-		}
184
+        $homeRule = $this->access->getConnection()->homeFolderNamingRule;
185
+        if(strpos($homeRule, 'attr:') === 0) {
186
+            $attributes[] = substr($homeRule, strlen('attr:'));
187
+        }
188 188
 
189
-		if(!$minimal) {
190
-			// attributes that are not really important but may come with big
191
-			// payload.
192
-			$attributes = array_merge($attributes, array(
193
-				'jpegphoto',
194
-				'thumbnailphoto'
195
-			));
196
-		}
189
+        if(!$minimal) {
190
+            // attributes that are not really important but may come with big
191
+            // payload.
192
+            $attributes = array_merge($attributes, array(
193
+                'jpegphoto',
194
+                'thumbnailphoto'
195
+            ));
196
+        }
197 197
 
198
-		return $attributes;
199
-	}
198
+        return $attributes;
199
+    }
200 200
 
201
-	/**
202
-	 * Checks whether the specified user is marked as deleted
203
-	 * @param string $id the Nextcloud user name
204
-	 * @return bool
205
-	 */
206
-	public function isDeletedUser($id) {
207
-		$isDeleted = $this->ocConfig->getUserValue(
208
-			$id, 'user_ldap', 'isDeleted', 0);
209
-		return intval($isDeleted) === 1;
210
-	}
201
+    /**
202
+     * Checks whether the specified user is marked as deleted
203
+     * @param string $id the Nextcloud user name
204
+     * @return bool
205
+     */
206
+    public function isDeletedUser($id) {
207
+        $isDeleted = $this->ocConfig->getUserValue(
208
+            $id, 'user_ldap', 'isDeleted', 0);
209
+        return intval($isDeleted) === 1;
210
+    }
211 211
 
212
-	/**
213
-	 * creates and returns an instance of OfflineUser for the specified user
214
-	 * @param string $id
215
-	 * @return \OCA\User_LDAP\User\OfflineUser
216
-	 */
217
-	public function getDeletedUser($id) {
218
-		return new OfflineUser(
219
-			$id,
220
-			$this->ocConfig,
221
-			$this->db,
222
-			$this->access->getUserMapper());
223
-	}
212
+    /**
213
+     * creates and returns an instance of OfflineUser for the specified user
214
+     * @param string $id
215
+     * @return \OCA\User_LDAP\User\OfflineUser
216
+     */
217
+    public function getDeletedUser($id) {
218
+        return new OfflineUser(
219
+            $id,
220
+            $this->ocConfig,
221
+            $this->db,
222
+            $this->access->getUserMapper());
223
+    }
224 224
 
225
-	/**
226
-	 * @brief returns a User object by it's Nextcloud username
227
-	 * @param string $id the DN or username of the user
228
-	 * @return \OCA\User_LDAP\User\User|\OCA\User_LDAP\User\OfflineUser|null
229
-	 */
230
-	protected function createInstancyByUserName($id) {
231
-		//most likely a uid. Check whether it is a deleted user
232
-		if($this->isDeletedUser($id)) {
233
-			return $this->getDeletedUser($id);
234
-		}
235
-		$dn = $this->access->username2dn($id);
236
-		if($dn !== false) {
237
-			return $this->createAndCache($dn, $id);
238
-		}
239
-		return null;
240
-	}
225
+    /**
226
+     * @brief returns a User object by it's Nextcloud username
227
+     * @param string $id the DN or username of the user
228
+     * @return \OCA\User_LDAP\User\User|\OCA\User_LDAP\User\OfflineUser|null
229
+     */
230
+    protected function createInstancyByUserName($id) {
231
+        //most likely a uid. Check whether it is a deleted user
232
+        if($this->isDeletedUser($id)) {
233
+            return $this->getDeletedUser($id);
234
+        }
235
+        $dn = $this->access->username2dn($id);
236
+        if($dn !== false) {
237
+            return $this->createAndCache($dn, $id);
238
+        }
239
+        return null;
240
+    }
241 241
 
242
-	/**
243
-	 * @brief returns a User object by it's DN or Nextcloud username
244
-	 * @param string $id the DN or username of the user
245
-	 * @return \OCA\User_LDAP\User\User|\OCA\User_LDAP\User\OfflineUser|null
246
-	 * @throws \Exception when connection could not be established
247
-	 */
248
-	public function get($id) {
249
-		$this->checkAccess();
250
-		if(isset($this->usersByDN[$id])) {
251
-			return $this->usersByDN[$id];
252
-		} else if(isset($this->usersByUid[$id])) {
253
-			return $this->usersByUid[$id];
254
-		}
242
+    /**
243
+     * @brief returns a User object by it's DN or Nextcloud username
244
+     * @param string $id the DN or username of the user
245
+     * @return \OCA\User_LDAP\User\User|\OCA\User_LDAP\User\OfflineUser|null
246
+     * @throws \Exception when connection could not be established
247
+     */
248
+    public function get($id) {
249
+        $this->checkAccess();
250
+        if(isset($this->usersByDN[$id])) {
251
+            return $this->usersByDN[$id];
252
+        } else if(isset($this->usersByUid[$id])) {
253
+            return $this->usersByUid[$id];
254
+        }
255 255
 
256
-		if($this->access->stringResemblesDN($id) ) {
257
-			$uid = $this->access->dn2username($id);
258
-			if($uid !== false) {
259
-				return $this->createAndCache($id, $uid);
260
-			}
261
-		}
256
+        if($this->access->stringResemblesDN($id) ) {
257
+            $uid = $this->access->dn2username($id);
258
+            if($uid !== false) {
259
+                return $this->createAndCache($id, $uid);
260
+            }
261
+        }
262 262
 
263
-		return $this->createInstancyByUserName($id);
264
-	}
263
+        return $this->createInstancyByUserName($id);
264
+    }
265 265
 
266 266
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/User/User.php 2 patches
Indentation   +644 added lines, -644 removed lines patch added patch discarded remove patch
@@ -43,654 +43,654 @@
 block discarded – undo
43 43
  * represents an LDAP user, gets and holds user-specific information from LDAP
44 44
  */
45 45
 class User {
46
-	/**
47
-	 * @var IUserTools
48
-	 */
49
-	protected $access;
50
-	/**
51
-	 * @var Connection
52
-	 */
53
-	protected $connection;
54
-	/**
55
-	 * @var IConfig
56
-	 */
57
-	protected $config;
58
-	/**
59
-	 * @var FilesystemHelper
60
-	 */
61
-	protected $fs;
62
-	/**
63
-	 * @var Image
64
-	 */
65
-	protected $image;
66
-	/**
67
-	 * @var LogWrapper
68
-	 */
69
-	protected $log;
70
-	/**
71
-	 * @var IAvatarManager
72
-	 */
73
-	protected $avatarManager;
74
-	/**
75
-	 * @var IUserManager
76
-	 */
77
-	protected $userManager;
78
-	/**
79
-	 * @var INotificationManager
80
-	 */
81
-	protected $notificationManager;
82
-	/**
83
-	 * @var string
84
-	 */
85
-	protected $dn;
86
-	/**
87
-	 * @var string
88
-	 */
89
-	protected $uid;
90
-	/**
91
-	 * @var string[]
92
-	 */
93
-	protected $refreshedFeatures = array();
94
-	/**
95
-	 * @var string
96
-	 */
97
-	protected $avatarImage;
98
-
99
-	/**
100
-	 * DB config keys for user preferences
101
-	 */
102
-	const USER_PREFKEY_FIRSTLOGIN  = 'firstLoginAccomplished';
103
-	const USER_PREFKEY_LASTREFRESH = 'lastFeatureRefresh';
104
-
105
-	/**
106
-	 * @brief constructor, make sure the subclasses call this one!
107
-	 * @param string $username the internal username
108
-	 * @param string $dn the LDAP DN
109
-	 * @param IUserTools $access an instance that implements IUserTools for
110
-	 * LDAP interaction
111
-	 * @param IConfig $config
112
-	 * @param FilesystemHelper $fs
113
-	 * @param Image $image any empty instance
114
-	 * @param LogWrapper $log
115
-	 * @param IAvatarManager $avatarManager
116
-	 * @param IUserManager $userManager
117
-	 * @param INotificationManager $notificationManager
118
-	 */
119
-	public function __construct($username, $dn, IUserTools $access,
120
-		IConfig $config, FilesystemHelper $fs, Image $image,
121
-		LogWrapper $log, IAvatarManager $avatarManager, IUserManager $userManager,
122
-		INotificationManager $notificationManager) {
46
+    /**
47
+     * @var IUserTools
48
+     */
49
+    protected $access;
50
+    /**
51
+     * @var Connection
52
+     */
53
+    protected $connection;
54
+    /**
55
+     * @var IConfig
56
+     */
57
+    protected $config;
58
+    /**
59
+     * @var FilesystemHelper
60
+     */
61
+    protected $fs;
62
+    /**
63
+     * @var Image
64
+     */
65
+    protected $image;
66
+    /**
67
+     * @var LogWrapper
68
+     */
69
+    protected $log;
70
+    /**
71
+     * @var IAvatarManager
72
+     */
73
+    protected $avatarManager;
74
+    /**
75
+     * @var IUserManager
76
+     */
77
+    protected $userManager;
78
+    /**
79
+     * @var INotificationManager
80
+     */
81
+    protected $notificationManager;
82
+    /**
83
+     * @var string
84
+     */
85
+    protected $dn;
86
+    /**
87
+     * @var string
88
+     */
89
+    protected $uid;
90
+    /**
91
+     * @var string[]
92
+     */
93
+    protected $refreshedFeatures = array();
94
+    /**
95
+     * @var string
96
+     */
97
+    protected $avatarImage;
98
+
99
+    /**
100
+     * DB config keys for user preferences
101
+     */
102
+    const USER_PREFKEY_FIRSTLOGIN  = 'firstLoginAccomplished';
103
+    const USER_PREFKEY_LASTREFRESH = 'lastFeatureRefresh';
104
+
105
+    /**
106
+     * @brief constructor, make sure the subclasses call this one!
107
+     * @param string $username the internal username
108
+     * @param string $dn the LDAP DN
109
+     * @param IUserTools $access an instance that implements IUserTools for
110
+     * LDAP interaction
111
+     * @param IConfig $config
112
+     * @param FilesystemHelper $fs
113
+     * @param Image $image any empty instance
114
+     * @param LogWrapper $log
115
+     * @param IAvatarManager $avatarManager
116
+     * @param IUserManager $userManager
117
+     * @param INotificationManager $notificationManager
118
+     */
119
+    public function __construct($username, $dn, IUserTools $access,
120
+        IConfig $config, FilesystemHelper $fs, Image $image,
121
+        LogWrapper $log, IAvatarManager $avatarManager, IUserManager $userManager,
122
+        INotificationManager $notificationManager) {
123 123
 	
124
-		if ($username === null) {
125
-			$log->log("uid for '$dn' must not be null!", Util::ERROR);
126
-			throw new \InvalidArgumentException('uid must not be null!');
127
-		} else if ($username === '') {
128
-			$log->log("uid for '$dn' must not be an empty string", Util::ERROR);
129
-			throw new \InvalidArgumentException('uid must not be an empty string!');
130
-		}
131
-
132
-		$this->access              = $access;
133
-		$this->connection          = $access->getConnection();
134
-		$this->config              = $config;
135
-		$this->fs                  = $fs;
136
-		$this->dn                  = $dn;
137
-		$this->uid                 = $username;
138
-		$this->image               = $image;
139
-		$this->log                 = $log;
140
-		$this->avatarManager       = $avatarManager;
141
-		$this->userManager         = $userManager;
142
-		$this->notificationManager = $notificationManager;
143
-
144
-		\OCP\Util::connectHook('OC_User', 'post_login', $this, 'handlePasswordExpiry');
145
-	}
146
-
147
-	/**
148
-	 * @brief updates properties like email, quota or avatar provided by LDAP
149
-	 * @return null
150
-	 */
151
-	public function update() {
152
-		if(is_null($this->dn)) {
153
-			return null;
154
-		}
155
-
156
-		$hasLoggedIn = $this->config->getUserValue($this->uid, 'user_ldap',
157
-				self::USER_PREFKEY_FIRSTLOGIN, 0);
158
-
159
-		if($this->needsRefresh()) {
160
-			$this->updateEmail();
161
-			$this->updateQuota();
162
-			if($hasLoggedIn !== 0) {
163
-				//we do not need to try it, when the user has not been logged in
164
-				//before, because the file system will not be ready.
165
-				$this->updateAvatar();
166
-				//in order to get an avatar as soon as possible, mark the user
167
-				//as refreshed only when updating the avatar did happen
168
-				$this->markRefreshTime();
169
-			}
170
-		}
171
-	}
172
-
173
-	/**
174
-	 * processes results from LDAP for attributes as returned by getAttributesToRead()
175
-	 * @param array $ldapEntry the user entry as retrieved from LDAP
176
-	 */
177
-	public function processAttributes($ldapEntry) {
178
-		$this->markRefreshTime();
179
-		//Quota
180
-		$attr = strtolower($this->connection->ldapQuotaAttribute);
181
-		if(isset($ldapEntry[$attr])) {
182
-			$this->updateQuota($ldapEntry[$attr][0]);
183
-		} else {
184
-			if ($this->connection->ldapQuotaDefault !== '') {
185
-				$this->updateQuota();
186
-			}
187
-		}
188
-		unset($attr);
189
-
190
-		//displayName
191
-		$displayName = $displayName2 = '';
192
-		$attr = strtolower($this->connection->ldapUserDisplayName);
193
-		if(isset($ldapEntry[$attr])) {
194
-			$displayName = strval($ldapEntry[$attr][0]);
195
-		}
196
-		$attr = strtolower($this->connection->ldapUserDisplayName2);
197
-		if(isset($ldapEntry[$attr])) {
198
-			$displayName2 = strval($ldapEntry[$attr][0]);
199
-		}
200
-		if ($displayName !== '') {
201
-			$this->composeAndStoreDisplayName($displayName);
202
-			$this->access->cacheUserDisplayName(
203
-				$this->getUsername(),
204
-				$displayName,
205
-				$displayName2
206
-			);
207
-		}
208
-		unset($attr);
209
-
210
-		//Email
211
-		//email must be stored after displayname, because it would cause a user
212
-		//change event that will trigger fetching the display name again
213
-		$attr = strtolower($this->connection->ldapEmailAttribute);
214
-		if(isset($ldapEntry[$attr])) {
215
-			$this->updateEmail($ldapEntry[$attr][0]);
216
-		}
217
-		unset($attr);
218
-
219
-		// LDAP Username, needed for s2s sharing
220
-		if(isset($ldapEntry['uid'])) {
221
-			$this->storeLDAPUserName($ldapEntry['uid'][0]);
222
-		} else if(isset($ldapEntry['samaccountname'])) {
223
-			$this->storeLDAPUserName($ldapEntry['samaccountname'][0]);
224
-		}
225
-
226
-		//homePath
227
-		if(strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) {
228
-			$attr = strtolower(substr($this->connection->homeFolderNamingRule, strlen('attr:')));
229
-			if(isset($ldapEntry[$attr])) {
230
-				$this->access->cacheUserHome(
231
-					$this->getUsername(), $this->getHomePath($ldapEntry[$attr][0]));
232
-			}
233
-		}
234
-
235
-		//memberOf groups
236
-		$cacheKey = 'getMemberOf'.$this->getUsername();
237
-		$groups = false;
238
-		if(isset($ldapEntry['memberof'])) {
239
-			$groups = $ldapEntry['memberof'];
240
-		}
241
-		$this->connection->writeToCache($cacheKey, $groups);
242
-
243
-		//Avatar
244
-		$attrs = array('jpegphoto', 'thumbnailphoto');
245
-		foreach ($attrs as $attr)  {
246
-			if(isset($ldapEntry[$attr])) {
247
-				$this->avatarImage = $ldapEntry[$attr][0];
248
-				// the call to the method that saves the avatar in the file
249
-				// system must be postponed after the login. It is to ensure
250
-				// external mounts are mounted properly (e.g. with login
251
-				// credentials from the session).
252
-				\OCP\Util::connectHook('OC_User', 'post_login', $this, 'updateAvatarPostLogin');
253
-				break;
254
-			}
255
-		}
256
-	}
257
-
258
-	/**
259
-	 * @brief returns the LDAP DN of the user
260
-	 * @return string
261
-	 */
262
-	public function getDN() {
263
-		return $this->dn;
264
-	}
265
-
266
-	/**
267
-	 * @brief returns the Nextcloud internal username of the user
268
-	 * @return string
269
-	 */
270
-	public function getUsername() {
271
-		return $this->uid;
272
-	}
273
-
274
-	/**
275
-	 * returns the home directory of the user if specified by LDAP settings
276
-	 * @param string $valueFromLDAP
277
-	 * @return bool|string
278
-	 * @throws \Exception
279
-	 */
280
-	public function getHomePath($valueFromLDAP = null) {
281
-		$path = strval($valueFromLDAP);
282
-		$attr = null;
283
-
284
-		if (is_null($valueFromLDAP)
285
-		   && strpos($this->access->connection->homeFolderNamingRule, 'attr:') === 0
286
-		   && $this->access->connection->homeFolderNamingRule !== 'attr:')
287
-		{
288
-			$attr = substr($this->access->connection->homeFolderNamingRule, strlen('attr:'));
289
-			$homedir = $this->access->readAttribute(
290
-				$this->access->username2dn($this->getUsername()), $attr);
291
-			if ($homedir && isset($homedir[0])) {
292
-				$path = $homedir[0];
293
-			}
294
-		}
295
-
296
-		if ($path !== '') {
297
-			//if attribute's value is an absolute path take this, otherwise append it to data dir
298
-			//check for / at the beginning or pattern c:\ resp. c:/
299
-			if(   '/' !== $path[0]
300
-			   && !(3 < strlen($path) && ctype_alpha($path[0])
301
-			       && $path[1] === ':' && ('\\' === $path[2] || '/' === $path[2]))
302
-			) {
303
-				$path = $this->config->getSystemValue('datadirectory',
304
-						\OC::$SERVERROOT.'/data' ) . '/' . $path;
305
-			}
306
-			//we need it to store it in the DB as well in case a user gets
307
-			//deleted so we can clean up afterwards
308
-			$this->config->setUserValue(
309
-				$this->getUsername(), 'user_ldap', 'homePath', $path
310
-			);
311
-			return $path;
312
-		}
313
-
314
-		if(    !is_null($attr)
315
-			&& $this->config->getAppValue('user_ldap', 'enforce_home_folder_naming_rule', true)
316
-		) {
317
-			// a naming rule attribute is defined, but it doesn't exist for that LDAP user
318
-			throw new \Exception('Home dir attribute can\'t be read from LDAP for uid: ' . $this->getUsername());
319
-		}
320
-
321
-		//false will apply default behaviour as defined and done by OC_User
322
-		$this->config->setUserValue($this->getUsername(), 'user_ldap', 'homePath', '');
323
-		return false;
324
-	}
325
-
326
-	public function getMemberOfGroups() {
327
-		$cacheKey = 'getMemberOf'.$this->getUsername();
328
-		$memberOfGroups = $this->connection->getFromCache($cacheKey);
329
-		if(!is_null($memberOfGroups)) {
330
-			return $memberOfGroups;
331
-		}
332
-		$groupDNs = $this->access->readAttribute($this->getDN(), 'memberOf');
333
-		$this->connection->writeToCache($cacheKey, $groupDNs);
334
-		return $groupDNs;
335
-	}
336
-
337
-	/**
338
-	 * @brief reads the image from LDAP that shall be used as Avatar
339
-	 * @return string data (provided by LDAP) | false
340
-	 */
341
-	public function getAvatarImage() {
342
-		if(!is_null($this->avatarImage)) {
343
-			return $this->avatarImage;
344
-		}
345
-
346
-		$this->avatarImage = false;
347
-		$attributes = array('jpegPhoto', 'thumbnailPhoto');
348
-		foreach($attributes as $attribute) {
349
-			$result = $this->access->readAttribute($this->dn, $attribute);
350
-			if($result !== false && is_array($result) && isset($result[0])) {
351
-				$this->avatarImage = $result[0];
352
-				break;
353
-			}
354
-		}
355
-
356
-		return $this->avatarImage;
357
-	}
358
-
359
-	/**
360
-	 * @brief marks the user as having logged in at least once
361
-	 * @return null
362
-	 */
363
-	public function markLogin() {
364
-		$this->config->setUserValue(
365
-			$this->uid, 'user_ldap', self::USER_PREFKEY_FIRSTLOGIN, 1);
366
-	}
367
-
368
-	/**
369
-	 * @brief marks the time when user features like email have been updated
370
-	 * @return null
371
-	 */
372
-	public function markRefreshTime() {
373
-		$this->config->setUserValue(
374
-			$this->uid, 'user_ldap', self::USER_PREFKEY_LASTREFRESH, time());
375
-	}
376
-
377
-	/**
378
-	 * @brief checks whether user features needs to be updated again by
379
-	 * comparing the difference of time of the last refresh to now with the
380
-	 * desired interval
381
-	 * @return bool
382
-	 */
383
-	private function needsRefresh() {
384
-		$lastChecked = $this->config->getUserValue($this->uid, 'user_ldap',
385
-			self::USER_PREFKEY_LASTREFRESH, 0);
386
-
387
-		//TODO make interval configurable
388
-		if((time() - intval($lastChecked)) < 86400 ) {
389
-			return false;
390
-		}
391
-		return  true;
392
-	}
393
-
394
-	/**
395
-	 * Stores a key-value pair in relation to this user
396
-	 *
397
-	 * @param string $key
398
-	 * @param string $value
399
-	 */
400
-	private function store($key, $value) {
401
-		$this->config->setUserValue($this->uid, 'user_ldap', $key, $value);
402
-	}
403
-
404
-	/**
405
-	 * Composes the display name and stores it in the database. The final
406
-	 * display name is returned.
407
-	 *
408
-	 * @param string $displayName
409
-	 * @param string $displayName2
410
-	 * @returns string the effective display name
411
-	 */
412
-	public function composeAndStoreDisplayName($displayName, $displayName2 = '') {
413
-		$displayName2 = strval($displayName2);
414
-		if($displayName2 !== '') {
415
-			$displayName .= ' (' . $displayName2 . ')';
416
-		}
417
-		$this->store('displayName', $displayName);
418
-		return $displayName;
419
-	}
420
-
421
-	/**
422
-	 * Stores the LDAP Username in the Database
423
-	 * @param string $userName
424
-	 */
425
-	public function storeLDAPUserName($userName) {
426
-		$this->store('uid', $userName);
427
-	}
428
-
429
-	/**
430
-	 * @brief checks whether an update method specified by feature was run
431
-	 * already. If not, it will marked like this, because it is expected that
432
-	 * the method will be run, when false is returned.
433
-	 * @param string $feature email | quota | avatar (can be extended)
434
-	 * @return bool
435
-	 */
436
-	private function wasRefreshed($feature) {
437
-		if(isset($this->refreshedFeatures[$feature])) {
438
-			return true;
439
-		}
440
-		$this->refreshedFeatures[$feature] = 1;
441
-		return false;
442
-	}
443
-
444
-	/**
445
-	 * fetches the email from LDAP and stores it as Nextcloud user value
446
-	 * @param string $valueFromLDAP if known, to save an LDAP read request
447
-	 * @return null
448
-	 */
449
-	public function updateEmail($valueFromLDAP = null) {
450
-		if($this->wasRefreshed('email')) {
451
-			return;
452
-		}
453
-		$email = strval($valueFromLDAP);
454
-		if(is_null($valueFromLDAP)) {
455
-			$emailAttribute = $this->connection->ldapEmailAttribute;
456
-			if ($emailAttribute !== '') {
457
-				$aEmail = $this->access->readAttribute($this->dn, $emailAttribute);
458
-				if(is_array($aEmail) && (count($aEmail) > 0)) {
459
-					$email = strval($aEmail[0]);
460
-				}
461
-			}
462
-		}
463
-		if ($email !== '') {
464
-			$user = $this->userManager->get($this->uid);
465
-			if (!is_null($user)) {
466
-				$currentEmail = strval($user->getEMailAddress());
467
-				if ($currentEmail !== $email) {
468
-					$user->setEMailAddress($email);
469
-				}
470
-			}
471
-		}
472
-	}
473
-
474
-	/**
475
-	 * Overall process goes as follow:
476
-	 * 1. fetch the quota from LDAP and check if it's parseable with the "verifyQuotaValue" function
477
-	 * 2. if the value can't be fetched, is empty or not parseable, use the default LDAP quota
478
-	 * 3. if the default LDAP quota can't be parsed, use the Nextcloud's default quota (use 'default')
479
-	 * 4. check if the target user exists and set the quota for the user.
480
-	 *
481
-	 * In order to improve performance and prevent an unwanted extra LDAP call, the $valueFromLDAP
482
-	 * parameter can be passed with the value of the attribute. This value will be considered as the
483
-	 * quota for the user coming from the LDAP server (step 1 of the process) It can be useful to
484
-	 * fetch all the user's attributes in one call and use the fetched values in this function.
485
-	 * The expected value for that parameter is a string describing the quota for the user. Valid
486
-	 * values are 'none' (unlimited), 'default' (the Nextcloud's default quota), '1234' (quota in
487
-	 * bytes), '1234 MB' (quota in MB - check the \OC_Helper::computerFileSize method for more info)
488
-	 *
489
-	 * fetches the quota from LDAP and stores it as Nextcloud user value
490
-	 * @param string $valueFromLDAP the quota attribute's value can be passed,
491
-	 * to save the readAttribute request
492
-	 * @return null
493
-	 */
494
-	public function updateQuota($valueFromLDAP = null) {
495
-		if($this->wasRefreshed('quota')) {
496
-			return;
497
-		}
498
-
499
-		$quota = false;
500
-		if(is_null($valueFromLDAP)) {
501
-			$quotaAttribute = $this->connection->ldapQuotaAttribute;
502
-			if ($quotaAttribute !== '') {
503
-				$aQuota = $this->access->readAttribute($this->dn, $quotaAttribute);
504
-				if($aQuota && (count($aQuota) > 0)) {
505
-					if ($this->verifyQuotaValue($aQuota[0])) {
506
-						$quota = $aQuota[0];
507
-					} else {
508
-						$this->log->log('not suitable LDAP quota found for user ' . $this->uid . ': [' . $aQuota[0] . ']', \OCP\Util::WARN);
509
-					}
510
-				}
511
-			}
512
-		} else {
513
-			if ($this->verifyQuotaValue($valueFromLDAP)) {
514
-				$quota = $valueFromLDAP;
515
-			} else {
516
-				$this->log->log('not suitable LDAP quota found for user ' . $this->uid . ': [' . $valueFromLDAP . ']', \OCP\Util::WARN);
517
-			}
518
-		}
519
-
520
-		if ($quota === false) {
521
-			// quota not found using the LDAP attribute (or not parseable). Try the default quota
522
-			$defaultQuota = $this->connection->ldapQuotaDefault;
523
-			if ($this->verifyQuotaValue($defaultQuota)) {
524
-				$quota = $defaultQuota;
525
-			}
526
-		}
527
-
528
-		$targetUser = $this->userManager->get($this->uid);
529
-		if ($targetUser) {
530
-			if($quota !== false) {
531
-				$targetUser->setQuota($quota);
532
-			} else {
533
-				$this->log->log('not suitable default quota found for user ' . $this->uid . ': [' . $defaultQuota . ']', \OCP\Util::WARN);
534
-			}
535
-		} else {
536
-			$this->log->log('trying to set a quota for user ' . $this->uid . ' but the user is missing', \OCP\Util::ERROR);
537
-		}
538
-	}
539
-
540
-	private function verifyQuotaValue($quotaValue) {
541
-		return $quotaValue === 'none' || $quotaValue === 'default' || \OC_Helper::computerFileSize($quotaValue) !== false;
542
-	}
543
-
544
-	/**
545
-	 * called by a post_login hook to save the avatar picture
546
-	 *
547
-	 * @param array $params
548
-	 */
549
-	public function updateAvatarPostLogin($params) {
550
-		if(isset($params['uid']) && $params['uid'] === $this->getUsername()) {
551
-			$this->updateAvatar();
552
-		}
553
-	}
554
-
555
-	/**
556
-	 * @brief attempts to get an image from LDAP and sets it as Nextcloud avatar
557
-	 * @return null
558
-	 */
559
-	public function updateAvatar() {
560
-		if($this->wasRefreshed('avatar')) {
561
-			return;
562
-		}
563
-		$avatarImage = $this->getAvatarImage();
564
-		if($avatarImage === false) {
565
-			//not set, nothing left to do;
566
-			return;
567
-		}
568
-		$this->image->loadFromBase64(base64_encode($avatarImage));
569
-		$this->setOwnCloudAvatar();
570
-	}
571
-
572
-	/**
573
-	 * @brief sets an image as Nextcloud avatar
574
-	 * @return null
575
-	 */
576
-	private function setOwnCloudAvatar() {
577
-		if(!$this->image->valid()) {
578
-			$this->log->log('jpegPhoto data invalid for '.$this->dn, \OCP\Util::ERROR);
579
-			return;
580
-		}
581
-		//make sure it is a square and not bigger than 128x128
582
-		$size = min(array($this->image->width(), $this->image->height(), 128));
583
-		if(!$this->image->centerCrop($size)) {
584
-			$this->log->log('croping image for avatar failed for '.$this->dn, \OCP\Util::ERROR);
585
-			return;
586
-		}
587
-
588
-		if(!$this->fs->isLoaded()) {
589
-			$this->fs->setup($this->uid);
590
-		}
591
-
592
-		try {
593
-			$avatar = $this->avatarManager->getAvatar($this->uid);
594
-			$avatar->set($this->image);
595
-		} catch (\Exception $e) {
596
-			\OC::$server->getLogger()->notice(
597
-				'Could not set avatar for ' . $this->dn	. ', because: ' . $e->getMessage(),
598
-				['app' => 'user_ldap']);
599
-		}
600
-	}
601
-
602
-	/**
603
-	 * called by a post_login hook to handle password expiry
604
-	 *
605
-	 * @param array $params
606
-	 */
607
-	public function handlePasswordExpiry($params) {
608
-		$ppolicyDN = $this->connection->ldapDefaultPPolicyDN;
609
-		if (empty($ppolicyDN) || (intval($this->connection->turnOnPasswordChange) !== 1)) {
610
-			return;//password expiry handling disabled
611
-		}
612
-		$uid = $params['uid'];
613
-		if(isset($uid) && $uid === $this->getUsername()) {
614
-			//retrieve relevant user attributes
615
-			$result = $this->access->search('objectclass=*', $this->dn, ['pwdpolicysubentry', 'pwdgraceusetime', 'pwdreset', 'pwdchangedtime']);
124
+        if ($username === null) {
125
+            $log->log("uid for '$dn' must not be null!", Util::ERROR);
126
+            throw new \InvalidArgumentException('uid must not be null!');
127
+        } else if ($username === '') {
128
+            $log->log("uid for '$dn' must not be an empty string", Util::ERROR);
129
+            throw new \InvalidArgumentException('uid must not be an empty string!');
130
+        }
131
+
132
+        $this->access              = $access;
133
+        $this->connection          = $access->getConnection();
134
+        $this->config              = $config;
135
+        $this->fs                  = $fs;
136
+        $this->dn                  = $dn;
137
+        $this->uid                 = $username;
138
+        $this->image               = $image;
139
+        $this->log                 = $log;
140
+        $this->avatarManager       = $avatarManager;
141
+        $this->userManager         = $userManager;
142
+        $this->notificationManager = $notificationManager;
143
+
144
+        \OCP\Util::connectHook('OC_User', 'post_login', $this, 'handlePasswordExpiry');
145
+    }
146
+
147
+    /**
148
+     * @brief updates properties like email, quota or avatar provided by LDAP
149
+     * @return null
150
+     */
151
+    public function update() {
152
+        if(is_null($this->dn)) {
153
+            return null;
154
+        }
155
+
156
+        $hasLoggedIn = $this->config->getUserValue($this->uid, 'user_ldap',
157
+                self::USER_PREFKEY_FIRSTLOGIN, 0);
158
+
159
+        if($this->needsRefresh()) {
160
+            $this->updateEmail();
161
+            $this->updateQuota();
162
+            if($hasLoggedIn !== 0) {
163
+                //we do not need to try it, when the user has not been logged in
164
+                //before, because the file system will not be ready.
165
+                $this->updateAvatar();
166
+                //in order to get an avatar as soon as possible, mark the user
167
+                //as refreshed only when updating the avatar did happen
168
+                $this->markRefreshTime();
169
+            }
170
+        }
171
+    }
172
+
173
+    /**
174
+     * processes results from LDAP for attributes as returned by getAttributesToRead()
175
+     * @param array $ldapEntry the user entry as retrieved from LDAP
176
+     */
177
+    public function processAttributes($ldapEntry) {
178
+        $this->markRefreshTime();
179
+        //Quota
180
+        $attr = strtolower($this->connection->ldapQuotaAttribute);
181
+        if(isset($ldapEntry[$attr])) {
182
+            $this->updateQuota($ldapEntry[$attr][0]);
183
+        } else {
184
+            if ($this->connection->ldapQuotaDefault !== '') {
185
+                $this->updateQuota();
186
+            }
187
+        }
188
+        unset($attr);
189
+
190
+        //displayName
191
+        $displayName = $displayName2 = '';
192
+        $attr = strtolower($this->connection->ldapUserDisplayName);
193
+        if(isset($ldapEntry[$attr])) {
194
+            $displayName = strval($ldapEntry[$attr][0]);
195
+        }
196
+        $attr = strtolower($this->connection->ldapUserDisplayName2);
197
+        if(isset($ldapEntry[$attr])) {
198
+            $displayName2 = strval($ldapEntry[$attr][0]);
199
+        }
200
+        if ($displayName !== '') {
201
+            $this->composeAndStoreDisplayName($displayName);
202
+            $this->access->cacheUserDisplayName(
203
+                $this->getUsername(),
204
+                $displayName,
205
+                $displayName2
206
+            );
207
+        }
208
+        unset($attr);
209
+
210
+        //Email
211
+        //email must be stored after displayname, because it would cause a user
212
+        //change event that will trigger fetching the display name again
213
+        $attr = strtolower($this->connection->ldapEmailAttribute);
214
+        if(isset($ldapEntry[$attr])) {
215
+            $this->updateEmail($ldapEntry[$attr][0]);
216
+        }
217
+        unset($attr);
218
+
219
+        // LDAP Username, needed for s2s sharing
220
+        if(isset($ldapEntry['uid'])) {
221
+            $this->storeLDAPUserName($ldapEntry['uid'][0]);
222
+        } else if(isset($ldapEntry['samaccountname'])) {
223
+            $this->storeLDAPUserName($ldapEntry['samaccountname'][0]);
224
+        }
225
+
226
+        //homePath
227
+        if(strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) {
228
+            $attr = strtolower(substr($this->connection->homeFolderNamingRule, strlen('attr:')));
229
+            if(isset($ldapEntry[$attr])) {
230
+                $this->access->cacheUserHome(
231
+                    $this->getUsername(), $this->getHomePath($ldapEntry[$attr][0]));
232
+            }
233
+        }
234
+
235
+        //memberOf groups
236
+        $cacheKey = 'getMemberOf'.$this->getUsername();
237
+        $groups = false;
238
+        if(isset($ldapEntry['memberof'])) {
239
+            $groups = $ldapEntry['memberof'];
240
+        }
241
+        $this->connection->writeToCache($cacheKey, $groups);
242
+
243
+        //Avatar
244
+        $attrs = array('jpegphoto', 'thumbnailphoto');
245
+        foreach ($attrs as $attr)  {
246
+            if(isset($ldapEntry[$attr])) {
247
+                $this->avatarImage = $ldapEntry[$attr][0];
248
+                // the call to the method that saves the avatar in the file
249
+                // system must be postponed after the login. It is to ensure
250
+                // external mounts are mounted properly (e.g. with login
251
+                // credentials from the session).
252
+                \OCP\Util::connectHook('OC_User', 'post_login', $this, 'updateAvatarPostLogin');
253
+                break;
254
+            }
255
+        }
256
+    }
257
+
258
+    /**
259
+     * @brief returns the LDAP DN of the user
260
+     * @return string
261
+     */
262
+    public function getDN() {
263
+        return $this->dn;
264
+    }
265
+
266
+    /**
267
+     * @brief returns the Nextcloud internal username of the user
268
+     * @return string
269
+     */
270
+    public function getUsername() {
271
+        return $this->uid;
272
+    }
273
+
274
+    /**
275
+     * returns the home directory of the user if specified by LDAP settings
276
+     * @param string $valueFromLDAP
277
+     * @return bool|string
278
+     * @throws \Exception
279
+     */
280
+    public function getHomePath($valueFromLDAP = null) {
281
+        $path = strval($valueFromLDAP);
282
+        $attr = null;
283
+
284
+        if (is_null($valueFromLDAP)
285
+           && strpos($this->access->connection->homeFolderNamingRule, 'attr:') === 0
286
+           && $this->access->connection->homeFolderNamingRule !== 'attr:')
287
+        {
288
+            $attr = substr($this->access->connection->homeFolderNamingRule, strlen('attr:'));
289
+            $homedir = $this->access->readAttribute(
290
+                $this->access->username2dn($this->getUsername()), $attr);
291
+            if ($homedir && isset($homedir[0])) {
292
+                $path = $homedir[0];
293
+            }
294
+        }
295
+
296
+        if ($path !== '') {
297
+            //if attribute's value is an absolute path take this, otherwise append it to data dir
298
+            //check for / at the beginning or pattern c:\ resp. c:/
299
+            if(   '/' !== $path[0]
300
+               && !(3 < strlen($path) && ctype_alpha($path[0])
301
+                   && $path[1] === ':' && ('\\' === $path[2] || '/' === $path[2]))
302
+            ) {
303
+                $path = $this->config->getSystemValue('datadirectory',
304
+                        \OC::$SERVERROOT.'/data' ) . '/' . $path;
305
+            }
306
+            //we need it to store it in the DB as well in case a user gets
307
+            //deleted so we can clean up afterwards
308
+            $this->config->setUserValue(
309
+                $this->getUsername(), 'user_ldap', 'homePath', $path
310
+            );
311
+            return $path;
312
+        }
313
+
314
+        if(    !is_null($attr)
315
+            && $this->config->getAppValue('user_ldap', 'enforce_home_folder_naming_rule', true)
316
+        ) {
317
+            // a naming rule attribute is defined, but it doesn't exist for that LDAP user
318
+            throw new \Exception('Home dir attribute can\'t be read from LDAP for uid: ' . $this->getUsername());
319
+        }
320
+
321
+        //false will apply default behaviour as defined and done by OC_User
322
+        $this->config->setUserValue($this->getUsername(), 'user_ldap', 'homePath', '');
323
+        return false;
324
+    }
325
+
326
+    public function getMemberOfGroups() {
327
+        $cacheKey = 'getMemberOf'.$this->getUsername();
328
+        $memberOfGroups = $this->connection->getFromCache($cacheKey);
329
+        if(!is_null($memberOfGroups)) {
330
+            return $memberOfGroups;
331
+        }
332
+        $groupDNs = $this->access->readAttribute($this->getDN(), 'memberOf');
333
+        $this->connection->writeToCache($cacheKey, $groupDNs);
334
+        return $groupDNs;
335
+    }
336
+
337
+    /**
338
+     * @brief reads the image from LDAP that shall be used as Avatar
339
+     * @return string data (provided by LDAP) | false
340
+     */
341
+    public function getAvatarImage() {
342
+        if(!is_null($this->avatarImage)) {
343
+            return $this->avatarImage;
344
+        }
345
+
346
+        $this->avatarImage = false;
347
+        $attributes = array('jpegPhoto', 'thumbnailPhoto');
348
+        foreach($attributes as $attribute) {
349
+            $result = $this->access->readAttribute($this->dn, $attribute);
350
+            if($result !== false && is_array($result) && isset($result[0])) {
351
+                $this->avatarImage = $result[0];
352
+                break;
353
+            }
354
+        }
355
+
356
+        return $this->avatarImage;
357
+    }
358
+
359
+    /**
360
+     * @brief marks the user as having logged in at least once
361
+     * @return null
362
+     */
363
+    public function markLogin() {
364
+        $this->config->setUserValue(
365
+            $this->uid, 'user_ldap', self::USER_PREFKEY_FIRSTLOGIN, 1);
366
+    }
367
+
368
+    /**
369
+     * @brief marks the time when user features like email have been updated
370
+     * @return null
371
+     */
372
+    public function markRefreshTime() {
373
+        $this->config->setUserValue(
374
+            $this->uid, 'user_ldap', self::USER_PREFKEY_LASTREFRESH, time());
375
+    }
376
+
377
+    /**
378
+     * @brief checks whether user features needs to be updated again by
379
+     * comparing the difference of time of the last refresh to now with the
380
+     * desired interval
381
+     * @return bool
382
+     */
383
+    private function needsRefresh() {
384
+        $lastChecked = $this->config->getUserValue($this->uid, 'user_ldap',
385
+            self::USER_PREFKEY_LASTREFRESH, 0);
386
+
387
+        //TODO make interval configurable
388
+        if((time() - intval($lastChecked)) < 86400 ) {
389
+            return false;
390
+        }
391
+        return  true;
392
+    }
393
+
394
+    /**
395
+     * Stores a key-value pair in relation to this user
396
+     *
397
+     * @param string $key
398
+     * @param string $value
399
+     */
400
+    private function store($key, $value) {
401
+        $this->config->setUserValue($this->uid, 'user_ldap', $key, $value);
402
+    }
403
+
404
+    /**
405
+     * Composes the display name and stores it in the database. The final
406
+     * display name is returned.
407
+     *
408
+     * @param string $displayName
409
+     * @param string $displayName2
410
+     * @returns string the effective display name
411
+     */
412
+    public function composeAndStoreDisplayName($displayName, $displayName2 = '') {
413
+        $displayName2 = strval($displayName2);
414
+        if($displayName2 !== '') {
415
+            $displayName .= ' (' . $displayName2 . ')';
416
+        }
417
+        $this->store('displayName', $displayName);
418
+        return $displayName;
419
+    }
420
+
421
+    /**
422
+     * Stores the LDAP Username in the Database
423
+     * @param string $userName
424
+     */
425
+    public function storeLDAPUserName($userName) {
426
+        $this->store('uid', $userName);
427
+    }
428
+
429
+    /**
430
+     * @brief checks whether an update method specified by feature was run
431
+     * already. If not, it will marked like this, because it is expected that
432
+     * the method will be run, when false is returned.
433
+     * @param string $feature email | quota | avatar (can be extended)
434
+     * @return bool
435
+     */
436
+    private function wasRefreshed($feature) {
437
+        if(isset($this->refreshedFeatures[$feature])) {
438
+            return true;
439
+        }
440
+        $this->refreshedFeatures[$feature] = 1;
441
+        return false;
442
+    }
443
+
444
+    /**
445
+     * fetches the email from LDAP and stores it as Nextcloud user value
446
+     * @param string $valueFromLDAP if known, to save an LDAP read request
447
+     * @return null
448
+     */
449
+    public function updateEmail($valueFromLDAP = null) {
450
+        if($this->wasRefreshed('email')) {
451
+            return;
452
+        }
453
+        $email = strval($valueFromLDAP);
454
+        if(is_null($valueFromLDAP)) {
455
+            $emailAttribute = $this->connection->ldapEmailAttribute;
456
+            if ($emailAttribute !== '') {
457
+                $aEmail = $this->access->readAttribute($this->dn, $emailAttribute);
458
+                if(is_array($aEmail) && (count($aEmail) > 0)) {
459
+                    $email = strval($aEmail[0]);
460
+                }
461
+            }
462
+        }
463
+        if ($email !== '') {
464
+            $user = $this->userManager->get($this->uid);
465
+            if (!is_null($user)) {
466
+                $currentEmail = strval($user->getEMailAddress());
467
+                if ($currentEmail !== $email) {
468
+                    $user->setEMailAddress($email);
469
+                }
470
+            }
471
+        }
472
+    }
473
+
474
+    /**
475
+     * Overall process goes as follow:
476
+     * 1. fetch the quota from LDAP and check if it's parseable with the "verifyQuotaValue" function
477
+     * 2. if the value can't be fetched, is empty or not parseable, use the default LDAP quota
478
+     * 3. if the default LDAP quota can't be parsed, use the Nextcloud's default quota (use 'default')
479
+     * 4. check if the target user exists and set the quota for the user.
480
+     *
481
+     * In order to improve performance and prevent an unwanted extra LDAP call, the $valueFromLDAP
482
+     * parameter can be passed with the value of the attribute. This value will be considered as the
483
+     * quota for the user coming from the LDAP server (step 1 of the process) It can be useful to
484
+     * fetch all the user's attributes in one call and use the fetched values in this function.
485
+     * The expected value for that parameter is a string describing the quota for the user. Valid
486
+     * values are 'none' (unlimited), 'default' (the Nextcloud's default quota), '1234' (quota in
487
+     * bytes), '1234 MB' (quota in MB - check the \OC_Helper::computerFileSize method for more info)
488
+     *
489
+     * fetches the quota from LDAP and stores it as Nextcloud user value
490
+     * @param string $valueFromLDAP the quota attribute's value can be passed,
491
+     * to save the readAttribute request
492
+     * @return null
493
+     */
494
+    public function updateQuota($valueFromLDAP = null) {
495
+        if($this->wasRefreshed('quota')) {
496
+            return;
497
+        }
498
+
499
+        $quota = false;
500
+        if(is_null($valueFromLDAP)) {
501
+            $quotaAttribute = $this->connection->ldapQuotaAttribute;
502
+            if ($quotaAttribute !== '') {
503
+                $aQuota = $this->access->readAttribute($this->dn, $quotaAttribute);
504
+                if($aQuota && (count($aQuota) > 0)) {
505
+                    if ($this->verifyQuotaValue($aQuota[0])) {
506
+                        $quota = $aQuota[0];
507
+                    } else {
508
+                        $this->log->log('not suitable LDAP quota found for user ' . $this->uid . ': [' . $aQuota[0] . ']', \OCP\Util::WARN);
509
+                    }
510
+                }
511
+            }
512
+        } else {
513
+            if ($this->verifyQuotaValue($valueFromLDAP)) {
514
+                $quota = $valueFromLDAP;
515
+            } else {
516
+                $this->log->log('not suitable LDAP quota found for user ' . $this->uid . ': [' . $valueFromLDAP . ']', \OCP\Util::WARN);
517
+            }
518
+        }
519
+
520
+        if ($quota === false) {
521
+            // quota not found using the LDAP attribute (or not parseable). Try the default quota
522
+            $defaultQuota = $this->connection->ldapQuotaDefault;
523
+            if ($this->verifyQuotaValue($defaultQuota)) {
524
+                $quota = $defaultQuota;
525
+            }
526
+        }
527
+
528
+        $targetUser = $this->userManager->get($this->uid);
529
+        if ($targetUser) {
530
+            if($quota !== false) {
531
+                $targetUser->setQuota($quota);
532
+            } else {
533
+                $this->log->log('not suitable default quota found for user ' . $this->uid . ': [' . $defaultQuota . ']', \OCP\Util::WARN);
534
+            }
535
+        } else {
536
+            $this->log->log('trying to set a quota for user ' . $this->uid . ' but the user is missing', \OCP\Util::ERROR);
537
+        }
538
+    }
539
+
540
+    private function verifyQuotaValue($quotaValue) {
541
+        return $quotaValue === 'none' || $quotaValue === 'default' || \OC_Helper::computerFileSize($quotaValue) !== false;
542
+    }
543
+
544
+    /**
545
+     * called by a post_login hook to save the avatar picture
546
+     *
547
+     * @param array $params
548
+     */
549
+    public function updateAvatarPostLogin($params) {
550
+        if(isset($params['uid']) && $params['uid'] === $this->getUsername()) {
551
+            $this->updateAvatar();
552
+        }
553
+    }
554
+
555
+    /**
556
+     * @brief attempts to get an image from LDAP and sets it as Nextcloud avatar
557
+     * @return null
558
+     */
559
+    public function updateAvatar() {
560
+        if($this->wasRefreshed('avatar')) {
561
+            return;
562
+        }
563
+        $avatarImage = $this->getAvatarImage();
564
+        if($avatarImage === false) {
565
+            //not set, nothing left to do;
566
+            return;
567
+        }
568
+        $this->image->loadFromBase64(base64_encode($avatarImage));
569
+        $this->setOwnCloudAvatar();
570
+    }
571
+
572
+    /**
573
+     * @brief sets an image as Nextcloud avatar
574
+     * @return null
575
+     */
576
+    private function setOwnCloudAvatar() {
577
+        if(!$this->image->valid()) {
578
+            $this->log->log('jpegPhoto data invalid for '.$this->dn, \OCP\Util::ERROR);
579
+            return;
580
+        }
581
+        //make sure it is a square and not bigger than 128x128
582
+        $size = min(array($this->image->width(), $this->image->height(), 128));
583
+        if(!$this->image->centerCrop($size)) {
584
+            $this->log->log('croping image for avatar failed for '.$this->dn, \OCP\Util::ERROR);
585
+            return;
586
+        }
587
+
588
+        if(!$this->fs->isLoaded()) {
589
+            $this->fs->setup($this->uid);
590
+        }
591
+
592
+        try {
593
+            $avatar = $this->avatarManager->getAvatar($this->uid);
594
+            $avatar->set($this->image);
595
+        } catch (\Exception $e) {
596
+            \OC::$server->getLogger()->notice(
597
+                'Could not set avatar for ' . $this->dn	. ', because: ' . $e->getMessage(),
598
+                ['app' => 'user_ldap']);
599
+        }
600
+    }
601
+
602
+    /**
603
+     * called by a post_login hook to handle password expiry
604
+     *
605
+     * @param array $params
606
+     */
607
+    public function handlePasswordExpiry($params) {
608
+        $ppolicyDN = $this->connection->ldapDefaultPPolicyDN;
609
+        if (empty($ppolicyDN) || (intval($this->connection->turnOnPasswordChange) !== 1)) {
610
+            return;//password expiry handling disabled
611
+        }
612
+        $uid = $params['uid'];
613
+        if(isset($uid) && $uid === $this->getUsername()) {
614
+            //retrieve relevant user attributes
615
+            $result = $this->access->search('objectclass=*', $this->dn, ['pwdpolicysubentry', 'pwdgraceusetime', 'pwdreset', 'pwdchangedtime']);
616 616
 			
617
-			if(array_key_exists('pwdpolicysubentry', $result[0])) {
618
-				$pwdPolicySubentry = $result[0]['pwdpolicysubentry'];
619
-				if($pwdPolicySubentry && (count($pwdPolicySubentry) > 0)){
620
-					$ppolicyDN = $pwdPolicySubentry[0];//custom ppolicy DN
621
-				}
622
-			}
617
+            if(array_key_exists('pwdpolicysubentry', $result[0])) {
618
+                $pwdPolicySubentry = $result[0]['pwdpolicysubentry'];
619
+                if($pwdPolicySubentry && (count($pwdPolicySubentry) > 0)){
620
+                    $ppolicyDN = $pwdPolicySubentry[0];//custom ppolicy DN
621
+                }
622
+            }
623 623
 			
624
-			$pwdGraceUseTime = array_key_exists('pwdgraceusetime', $result[0]) ? $result[0]['pwdgraceusetime'] : null;
625
-			$pwdReset = array_key_exists('pwdreset', $result[0]) ? $result[0]['pwdreset'] : null;
626
-			$pwdChangedTime = array_key_exists('pwdchangedtime', $result[0]) ? $result[0]['pwdchangedtime'] : null;
624
+            $pwdGraceUseTime = array_key_exists('pwdgraceusetime', $result[0]) ? $result[0]['pwdgraceusetime'] : null;
625
+            $pwdReset = array_key_exists('pwdreset', $result[0]) ? $result[0]['pwdreset'] : null;
626
+            $pwdChangedTime = array_key_exists('pwdchangedtime', $result[0]) ? $result[0]['pwdchangedtime'] : null;
627 627
 			
628
-			//retrieve relevant password policy attributes
629
-			$cacheKey = 'ppolicyAttributes' . $ppolicyDN;
630
-			$result = $this->connection->getFromCache($cacheKey);
631
-			if(is_null($result)) {
632
-				$result = $this->access->search('objectclass=*', $ppolicyDN, ['pwdgraceauthnlimit', 'pwdmaxage', 'pwdexpirewarning']);
633
-				$this->connection->writeToCache($cacheKey, $result);
634
-			}
628
+            //retrieve relevant password policy attributes
629
+            $cacheKey = 'ppolicyAttributes' . $ppolicyDN;
630
+            $result = $this->connection->getFromCache($cacheKey);
631
+            if(is_null($result)) {
632
+                $result = $this->access->search('objectclass=*', $ppolicyDN, ['pwdgraceauthnlimit', 'pwdmaxage', 'pwdexpirewarning']);
633
+                $this->connection->writeToCache($cacheKey, $result);
634
+            }
635 635
 			
636
-			$pwdGraceAuthNLimit = array_key_exists('pwdgraceauthnlimit', $result[0]) ? $result[0]['pwdgraceauthnlimit'] : null;
637
-			$pwdMaxAge = array_key_exists('pwdmaxage', $result[0]) ? $result[0]['pwdmaxage'] : null;
638
-			$pwdExpireWarning = array_key_exists('pwdexpirewarning', $result[0]) ? $result[0]['pwdexpirewarning'] : null;
636
+            $pwdGraceAuthNLimit = array_key_exists('pwdgraceauthnlimit', $result[0]) ? $result[0]['pwdgraceauthnlimit'] : null;
637
+            $pwdMaxAge = array_key_exists('pwdmaxage', $result[0]) ? $result[0]['pwdmaxage'] : null;
638
+            $pwdExpireWarning = array_key_exists('pwdexpirewarning', $result[0]) ? $result[0]['pwdexpirewarning'] : null;
639 639
 			
640
-			//handle grace login
641
-			$pwdGraceUseTimeCount = count($pwdGraceUseTime);
642
-			if($pwdGraceUseTime && $pwdGraceUseTimeCount > 0) { //was this a grace login?
643
-				if($pwdGraceAuthNLimit 
644
-					&& (count($pwdGraceAuthNLimit) > 0)
645
-					&&($pwdGraceUseTimeCount < intval($pwdGraceAuthNLimit[0]))) { //at least one more grace login available?
646
-					$this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
647
-					header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
648
-					'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid)));
649
-				} else { //no more grace login available
650
-					header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
651
-					'user_ldap.renewPassword.showLoginFormInvalidPassword', array('user' => $uid)));
652
-				}
653
-				exit();
654
-			}
655
-			//handle pwdReset attribute
656
-			if($pwdReset && (count($pwdReset) > 0) && $pwdReset[0] === 'TRUE') { //user must change his password
657
-				$this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
658
-				header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
659
-				'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid)));
660
-				exit();
661
-			}
662
-			//handle password expiry warning
663
-			if($pwdChangedTime && (count($pwdChangedTime) > 0)) {
664
-				if($pwdMaxAge && (count($pwdMaxAge) > 0)
665
-					&& $pwdExpireWarning && (count($pwdExpireWarning) > 0)) {
666
-					$pwdMaxAgeInt = intval($pwdMaxAge[0]);
667
-					$pwdExpireWarningInt = intval($pwdExpireWarning[0]);
668
-					if($pwdMaxAgeInt > 0 && $pwdExpireWarningInt > 0){
669
-						$pwdChangedTimeDt = \DateTime::createFromFormat('YmdHisZ', $pwdChangedTime[0]);
670
-						$pwdChangedTimeDt->add(new \DateInterval('PT'.$pwdMaxAgeInt.'S'));
671
-						$currentDateTime = new \DateTime();
672
-						$secondsToExpiry = $pwdChangedTimeDt->getTimestamp() - $currentDateTime->getTimestamp();
673
-						if($secondsToExpiry <= $pwdExpireWarningInt) {
674
-							//remove last password expiry warning if any
675
-							$notification = $this->notificationManager->createNotification();
676
-							$notification->setApp('user_ldap')
677
-								->setUser($uid)
678
-								->setObject('pwd_exp_warn', $uid)
679
-							;
680
-							$this->notificationManager->markProcessed($notification);
681
-							//create new password expiry warning
682
-							$notification = $this->notificationManager->createNotification();
683
-							$notification->setApp('user_ldap')
684
-								->setUser($uid)
685
-								->setDateTime($currentDateTime)
686
-								->setObject('pwd_exp_warn', $uid) 
687
-								->setSubject('pwd_exp_warn_days', [(int) ceil($secondsToExpiry / 60 / 60 / 24)])
688
-							;
689
-							$this->notificationManager->notify($notification);
690
-						}
691
-					}
692
-				}
693
-			}
694
-		}
695
-	}
640
+            //handle grace login
641
+            $pwdGraceUseTimeCount = count($pwdGraceUseTime);
642
+            if($pwdGraceUseTime && $pwdGraceUseTimeCount > 0) { //was this a grace login?
643
+                if($pwdGraceAuthNLimit 
644
+                    && (count($pwdGraceAuthNLimit) > 0)
645
+                    &&($pwdGraceUseTimeCount < intval($pwdGraceAuthNLimit[0]))) { //at least one more grace login available?
646
+                    $this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
647
+                    header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
648
+                    'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid)));
649
+                } else { //no more grace login available
650
+                    header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
651
+                    'user_ldap.renewPassword.showLoginFormInvalidPassword', array('user' => $uid)));
652
+                }
653
+                exit();
654
+            }
655
+            //handle pwdReset attribute
656
+            if($pwdReset && (count($pwdReset) > 0) && $pwdReset[0] === 'TRUE') { //user must change his password
657
+                $this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
658
+                header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
659
+                'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid)));
660
+                exit();
661
+            }
662
+            //handle password expiry warning
663
+            if($pwdChangedTime && (count($pwdChangedTime) > 0)) {
664
+                if($pwdMaxAge && (count($pwdMaxAge) > 0)
665
+                    && $pwdExpireWarning && (count($pwdExpireWarning) > 0)) {
666
+                    $pwdMaxAgeInt = intval($pwdMaxAge[0]);
667
+                    $pwdExpireWarningInt = intval($pwdExpireWarning[0]);
668
+                    if($pwdMaxAgeInt > 0 && $pwdExpireWarningInt > 0){
669
+                        $pwdChangedTimeDt = \DateTime::createFromFormat('YmdHisZ', $pwdChangedTime[0]);
670
+                        $pwdChangedTimeDt->add(new \DateInterval('PT'.$pwdMaxAgeInt.'S'));
671
+                        $currentDateTime = new \DateTime();
672
+                        $secondsToExpiry = $pwdChangedTimeDt->getTimestamp() - $currentDateTime->getTimestamp();
673
+                        if($secondsToExpiry <= $pwdExpireWarningInt) {
674
+                            //remove last password expiry warning if any
675
+                            $notification = $this->notificationManager->createNotification();
676
+                            $notification->setApp('user_ldap')
677
+                                ->setUser($uid)
678
+                                ->setObject('pwd_exp_warn', $uid)
679
+                            ;
680
+                            $this->notificationManager->markProcessed($notification);
681
+                            //create new password expiry warning
682
+                            $notification = $this->notificationManager->createNotification();
683
+                            $notification->setApp('user_ldap')
684
+                                ->setUser($uid)
685
+                                ->setDateTime($currentDateTime)
686
+                                ->setObject('pwd_exp_warn', $uid) 
687
+                                ->setSubject('pwd_exp_warn_days', [(int) ceil($secondsToExpiry / 60 / 60 / 24)])
688
+                            ;
689
+                            $this->notificationManager->notify($notification);
690
+                        }
691
+                    }
692
+                }
693
+            }
694
+        }
695
+    }
696 696
 }
Please login to merge, or discard this patch.
Spacing   +58 added lines, -58 removed lines patch added patch discarded remove patch
@@ -149,17 +149,17 @@  discard block
 block discarded – undo
149 149
 	 * @return null
150 150
 	 */
151 151
 	public function update() {
152
-		if(is_null($this->dn)) {
152
+		if (is_null($this->dn)) {
153 153
 			return null;
154 154
 		}
155 155
 
156 156
 		$hasLoggedIn = $this->config->getUserValue($this->uid, 'user_ldap',
157 157
 				self::USER_PREFKEY_FIRSTLOGIN, 0);
158 158
 
159
-		if($this->needsRefresh()) {
159
+		if ($this->needsRefresh()) {
160 160
 			$this->updateEmail();
161 161
 			$this->updateQuota();
162
-			if($hasLoggedIn !== 0) {
162
+			if ($hasLoggedIn !== 0) {
163 163
 				//we do not need to try it, when the user has not been logged in
164 164
 				//before, because the file system will not be ready.
165 165
 				$this->updateAvatar();
@@ -178,7 +178,7 @@  discard block
 block discarded – undo
178 178
 		$this->markRefreshTime();
179 179
 		//Quota
180 180
 		$attr = strtolower($this->connection->ldapQuotaAttribute);
181
-		if(isset($ldapEntry[$attr])) {
181
+		if (isset($ldapEntry[$attr])) {
182 182
 			$this->updateQuota($ldapEntry[$attr][0]);
183 183
 		} else {
184 184
 			if ($this->connection->ldapQuotaDefault !== '') {
@@ -190,11 +190,11 @@  discard block
 block discarded – undo
190 190
 		//displayName
191 191
 		$displayName = $displayName2 = '';
192 192
 		$attr = strtolower($this->connection->ldapUserDisplayName);
193
-		if(isset($ldapEntry[$attr])) {
193
+		if (isset($ldapEntry[$attr])) {
194 194
 			$displayName = strval($ldapEntry[$attr][0]);
195 195
 		}
196 196
 		$attr = strtolower($this->connection->ldapUserDisplayName2);
197
-		if(isset($ldapEntry[$attr])) {
197
+		if (isset($ldapEntry[$attr])) {
198 198
 			$displayName2 = strval($ldapEntry[$attr][0]);
199 199
 		}
200 200
 		if ($displayName !== '') {
@@ -211,22 +211,22 @@  discard block
 block discarded – undo
211 211
 		//email must be stored after displayname, because it would cause a user
212 212
 		//change event that will trigger fetching the display name again
213 213
 		$attr = strtolower($this->connection->ldapEmailAttribute);
214
-		if(isset($ldapEntry[$attr])) {
214
+		if (isset($ldapEntry[$attr])) {
215 215
 			$this->updateEmail($ldapEntry[$attr][0]);
216 216
 		}
217 217
 		unset($attr);
218 218
 
219 219
 		// LDAP Username, needed for s2s sharing
220
-		if(isset($ldapEntry['uid'])) {
220
+		if (isset($ldapEntry['uid'])) {
221 221
 			$this->storeLDAPUserName($ldapEntry['uid'][0]);
222
-		} else if(isset($ldapEntry['samaccountname'])) {
222
+		} else if (isset($ldapEntry['samaccountname'])) {
223 223
 			$this->storeLDAPUserName($ldapEntry['samaccountname'][0]);
224 224
 		}
225 225
 
226 226
 		//homePath
227
-		if(strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) {
227
+		if (strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) {
228 228
 			$attr = strtolower(substr($this->connection->homeFolderNamingRule, strlen('attr:')));
229
-			if(isset($ldapEntry[$attr])) {
229
+			if (isset($ldapEntry[$attr])) {
230 230
 				$this->access->cacheUserHome(
231 231
 					$this->getUsername(), $this->getHomePath($ldapEntry[$attr][0]));
232 232
 			}
@@ -235,15 +235,15 @@  discard block
 block discarded – undo
235 235
 		//memberOf groups
236 236
 		$cacheKey = 'getMemberOf'.$this->getUsername();
237 237
 		$groups = false;
238
-		if(isset($ldapEntry['memberof'])) {
238
+		if (isset($ldapEntry['memberof'])) {
239 239
 			$groups = $ldapEntry['memberof'];
240 240
 		}
241 241
 		$this->connection->writeToCache($cacheKey, $groups);
242 242
 
243 243
 		//Avatar
244 244
 		$attrs = array('jpegphoto', 'thumbnailphoto');
245
-		foreach ($attrs as $attr)  {
246
-			if(isset($ldapEntry[$attr])) {
245
+		foreach ($attrs as $attr) {
246
+			if (isset($ldapEntry[$attr])) {
247 247
 				$this->avatarImage = $ldapEntry[$attr][0];
248 248
 				// the call to the method that saves the avatar in the file
249 249
 				// system must be postponed after the login. It is to ensure
@@ -296,12 +296,12 @@  discard block
 block discarded – undo
296 296
 		if ($path !== '') {
297 297
 			//if attribute's value is an absolute path take this, otherwise append it to data dir
298 298
 			//check for / at the beginning or pattern c:\ resp. c:/
299
-			if(   '/' !== $path[0]
299
+			if ('/' !== $path[0]
300 300
 			   && !(3 < strlen($path) && ctype_alpha($path[0])
301 301
 			       && $path[1] === ':' && ('\\' === $path[2] || '/' === $path[2]))
302 302
 			) {
303 303
 				$path = $this->config->getSystemValue('datadirectory',
304
-						\OC::$SERVERROOT.'/data' ) . '/' . $path;
304
+						\OC::$SERVERROOT.'/data').'/'.$path;
305 305
 			}
306 306
 			//we need it to store it in the DB as well in case a user gets
307 307
 			//deleted so we can clean up afterwards
@@ -311,11 +311,11 @@  discard block
 block discarded – undo
311 311
 			return $path;
312 312
 		}
313 313
 
314
-		if(    !is_null($attr)
314
+		if (!is_null($attr)
315 315
 			&& $this->config->getAppValue('user_ldap', 'enforce_home_folder_naming_rule', true)
316 316
 		) {
317 317
 			// a naming rule attribute is defined, but it doesn't exist for that LDAP user
318
-			throw new \Exception('Home dir attribute can\'t be read from LDAP for uid: ' . $this->getUsername());
318
+			throw new \Exception('Home dir attribute can\'t be read from LDAP for uid: '.$this->getUsername());
319 319
 		}
320 320
 
321 321
 		//false will apply default behaviour as defined and done by OC_User
@@ -326,7 +326,7 @@  discard block
 block discarded – undo
326 326
 	public function getMemberOfGroups() {
327 327
 		$cacheKey = 'getMemberOf'.$this->getUsername();
328 328
 		$memberOfGroups = $this->connection->getFromCache($cacheKey);
329
-		if(!is_null($memberOfGroups)) {
329
+		if (!is_null($memberOfGroups)) {
330 330
 			return $memberOfGroups;
331 331
 		}
332 332
 		$groupDNs = $this->access->readAttribute($this->getDN(), 'memberOf');
@@ -339,15 +339,15 @@  discard block
 block discarded – undo
339 339
 	 * @return string data (provided by LDAP) | false
340 340
 	 */
341 341
 	public function getAvatarImage() {
342
-		if(!is_null($this->avatarImage)) {
342
+		if (!is_null($this->avatarImage)) {
343 343
 			return $this->avatarImage;
344 344
 		}
345 345
 
346 346
 		$this->avatarImage = false;
347 347
 		$attributes = array('jpegPhoto', 'thumbnailPhoto');
348
-		foreach($attributes as $attribute) {
348
+		foreach ($attributes as $attribute) {
349 349
 			$result = $this->access->readAttribute($this->dn, $attribute);
350
-			if($result !== false && is_array($result) && isset($result[0])) {
350
+			if ($result !== false && is_array($result) && isset($result[0])) {
351 351
 				$this->avatarImage = $result[0];
352 352
 				break;
353 353
 			}
@@ -385,7 +385,7 @@  discard block
 block discarded – undo
385 385
 			self::USER_PREFKEY_LASTREFRESH, 0);
386 386
 
387 387
 		//TODO make interval configurable
388
-		if((time() - intval($lastChecked)) < 86400 ) {
388
+		if ((time() - intval($lastChecked)) < 86400) {
389 389
 			return false;
390 390
 		}
391 391
 		return  true;
@@ -411,8 +411,8 @@  discard block
 block discarded – undo
411 411
 	 */
412 412
 	public function composeAndStoreDisplayName($displayName, $displayName2 = '') {
413 413
 		$displayName2 = strval($displayName2);
414
-		if($displayName2 !== '') {
415
-			$displayName .= ' (' . $displayName2 . ')';
414
+		if ($displayName2 !== '') {
415
+			$displayName .= ' ('.$displayName2.')';
416 416
 		}
417 417
 		$this->store('displayName', $displayName);
418 418
 		return $displayName;
@@ -434,7 +434,7 @@  discard block
 block discarded – undo
434 434
 	 * @return bool
435 435
 	 */
436 436
 	private function wasRefreshed($feature) {
437
-		if(isset($this->refreshedFeatures[$feature])) {
437
+		if (isset($this->refreshedFeatures[$feature])) {
438 438
 			return true;
439 439
 		}
440 440
 		$this->refreshedFeatures[$feature] = 1;
@@ -447,15 +447,15 @@  discard block
 block discarded – undo
447 447
 	 * @return null
448 448
 	 */
449 449
 	public function updateEmail($valueFromLDAP = null) {
450
-		if($this->wasRefreshed('email')) {
450
+		if ($this->wasRefreshed('email')) {
451 451
 			return;
452 452
 		}
453 453
 		$email = strval($valueFromLDAP);
454
-		if(is_null($valueFromLDAP)) {
454
+		if (is_null($valueFromLDAP)) {
455 455
 			$emailAttribute = $this->connection->ldapEmailAttribute;
456 456
 			if ($emailAttribute !== '') {
457 457
 				$aEmail = $this->access->readAttribute($this->dn, $emailAttribute);
458
-				if(is_array($aEmail) && (count($aEmail) > 0)) {
458
+				if (is_array($aEmail) && (count($aEmail) > 0)) {
459 459
 					$email = strval($aEmail[0]);
460 460
 				}
461 461
 			}
@@ -492,20 +492,20 @@  discard block
 block discarded – undo
492 492
 	 * @return null
493 493
 	 */
494 494
 	public function updateQuota($valueFromLDAP = null) {
495
-		if($this->wasRefreshed('quota')) {
495
+		if ($this->wasRefreshed('quota')) {
496 496
 			return;
497 497
 		}
498 498
 
499 499
 		$quota = false;
500
-		if(is_null($valueFromLDAP)) {
500
+		if (is_null($valueFromLDAP)) {
501 501
 			$quotaAttribute = $this->connection->ldapQuotaAttribute;
502 502
 			if ($quotaAttribute !== '') {
503 503
 				$aQuota = $this->access->readAttribute($this->dn, $quotaAttribute);
504
-				if($aQuota && (count($aQuota) > 0)) {
504
+				if ($aQuota && (count($aQuota) > 0)) {
505 505
 					if ($this->verifyQuotaValue($aQuota[0])) {
506 506
 						$quota = $aQuota[0];
507 507
 					} else {
508
-						$this->log->log('not suitable LDAP quota found for user ' . $this->uid . ': [' . $aQuota[0] . ']', \OCP\Util::WARN);
508
+						$this->log->log('not suitable LDAP quota found for user '.$this->uid.': ['.$aQuota[0].']', \OCP\Util::WARN);
509 509
 					}
510 510
 				}
511 511
 			}
@@ -513,7 +513,7 @@  discard block
 block discarded – undo
513 513
 			if ($this->verifyQuotaValue($valueFromLDAP)) {
514 514
 				$quota = $valueFromLDAP;
515 515
 			} else {
516
-				$this->log->log('not suitable LDAP quota found for user ' . $this->uid . ': [' . $valueFromLDAP . ']', \OCP\Util::WARN);
516
+				$this->log->log('not suitable LDAP quota found for user '.$this->uid.': ['.$valueFromLDAP.']', \OCP\Util::WARN);
517 517
 			}
518 518
 		}
519 519
 
@@ -527,13 +527,13 @@  discard block
 block discarded – undo
527 527
 
528 528
 		$targetUser = $this->userManager->get($this->uid);
529 529
 		if ($targetUser) {
530
-			if($quota !== false) {
530
+			if ($quota !== false) {
531 531
 				$targetUser->setQuota($quota);
532 532
 			} else {
533
-				$this->log->log('not suitable default quota found for user ' . $this->uid . ': [' . $defaultQuota . ']', \OCP\Util::WARN);
533
+				$this->log->log('not suitable default quota found for user '.$this->uid.': ['.$defaultQuota.']', \OCP\Util::WARN);
534 534
 			}
535 535
 		} else {
536
-			$this->log->log('trying to set a quota for user ' . $this->uid . ' but the user is missing', \OCP\Util::ERROR);
536
+			$this->log->log('trying to set a quota for user '.$this->uid.' but the user is missing', \OCP\Util::ERROR);
537 537
 		}
538 538
 	}
539 539
 
@@ -547,7 +547,7 @@  discard block
 block discarded – undo
547 547
 	 * @param array $params
548 548
 	 */
549 549
 	public function updateAvatarPostLogin($params) {
550
-		if(isset($params['uid']) && $params['uid'] === $this->getUsername()) {
550
+		if (isset($params['uid']) && $params['uid'] === $this->getUsername()) {
551 551
 			$this->updateAvatar();
552 552
 		}
553 553
 	}
@@ -557,11 +557,11 @@  discard block
 block discarded – undo
557 557
 	 * @return null
558 558
 	 */
559 559
 	public function updateAvatar() {
560
-		if($this->wasRefreshed('avatar')) {
560
+		if ($this->wasRefreshed('avatar')) {
561 561
 			return;
562 562
 		}
563 563
 		$avatarImage = $this->getAvatarImage();
564
-		if($avatarImage === false) {
564
+		if ($avatarImage === false) {
565 565
 			//not set, nothing left to do;
566 566
 			return;
567 567
 		}
@@ -574,18 +574,18 @@  discard block
 block discarded – undo
574 574
 	 * @return null
575 575
 	 */
576 576
 	private function setOwnCloudAvatar() {
577
-		if(!$this->image->valid()) {
577
+		if (!$this->image->valid()) {
578 578
 			$this->log->log('jpegPhoto data invalid for '.$this->dn, \OCP\Util::ERROR);
579 579
 			return;
580 580
 		}
581 581
 		//make sure it is a square and not bigger than 128x128
582 582
 		$size = min(array($this->image->width(), $this->image->height(), 128));
583
-		if(!$this->image->centerCrop($size)) {
583
+		if (!$this->image->centerCrop($size)) {
584 584
 			$this->log->log('croping image for avatar failed for '.$this->dn, \OCP\Util::ERROR);
585 585
 			return;
586 586
 		}
587 587
 
588
-		if(!$this->fs->isLoaded()) {
588
+		if (!$this->fs->isLoaded()) {
589 589
 			$this->fs->setup($this->uid);
590 590
 		}
591 591
 
@@ -594,7 +594,7 @@  discard block
 block discarded – undo
594 594
 			$avatar->set($this->image);
595 595
 		} catch (\Exception $e) {
596 596
 			\OC::$server->getLogger()->notice(
597
-				'Could not set avatar for ' . $this->dn	. ', because: ' . $e->getMessage(),
597
+				'Could not set avatar for '.$this->dn.', because: '.$e->getMessage(),
598 598
 				['app' => 'user_ldap']);
599 599
 		}
600 600
 	}
@@ -607,17 +607,17 @@  discard block
 block discarded – undo
607 607
 	public function handlePasswordExpiry($params) {
608 608
 		$ppolicyDN = $this->connection->ldapDefaultPPolicyDN;
609 609
 		if (empty($ppolicyDN) || (intval($this->connection->turnOnPasswordChange) !== 1)) {
610
-			return;//password expiry handling disabled
610
+			return; //password expiry handling disabled
611 611
 		}
612 612
 		$uid = $params['uid'];
613
-		if(isset($uid) && $uid === $this->getUsername()) {
613
+		if (isset($uid) && $uid === $this->getUsername()) {
614 614
 			//retrieve relevant user attributes
615 615
 			$result = $this->access->search('objectclass=*', $this->dn, ['pwdpolicysubentry', 'pwdgraceusetime', 'pwdreset', 'pwdchangedtime']);
616 616
 			
617
-			if(array_key_exists('pwdpolicysubentry', $result[0])) {
617
+			if (array_key_exists('pwdpolicysubentry', $result[0])) {
618 618
 				$pwdPolicySubentry = $result[0]['pwdpolicysubentry'];
619
-				if($pwdPolicySubentry && (count($pwdPolicySubentry) > 0)){
620
-					$ppolicyDN = $pwdPolicySubentry[0];//custom ppolicy DN
619
+				if ($pwdPolicySubentry && (count($pwdPolicySubentry) > 0)) {
620
+					$ppolicyDN = $pwdPolicySubentry[0]; //custom ppolicy DN
621 621
 				}
622 622
 			}
623 623
 			
@@ -626,9 +626,9 @@  discard block
 block discarded – undo
626 626
 			$pwdChangedTime = array_key_exists('pwdchangedtime', $result[0]) ? $result[0]['pwdchangedtime'] : null;
627 627
 			
628 628
 			//retrieve relevant password policy attributes
629
-			$cacheKey = 'ppolicyAttributes' . $ppolicyDN;
629
+			$cacheKey = 'ppolicyAttributes'.$ppolicyDN;
630 630
 			$result = $this->connection->getFromCache($cacheKey);
631
-			if(is_null($result)) {
631
+			if (is_null($result)) {
632 632
 				$result = $this->access->search('objectclass=*', $ppolicyDN, ['pwdgraceauthnlimit', 'pwdmaxage', 'pwdexpirewarning']);
633 633
 				$this->connection->writeToCache($cacheKey, $result);
634 634
 			}
@@ -639,8 +639,8 @@  discard block
 block discarded – undo
639 639
 			
640 640
 			//handle grace login
641 641
 			$pwdGraceUseTimeCount = count($pwdGraceUseTime);
642
-			if($pwdGraceUseTime && $pwdGraceUseTimeCount > 0) { //was this a grace login?
643
-				if($pwdGraceAuthNLimit 
642
+			if ($pwdGraceUseTime && $pwdGraceUseTimeCount > 0) { //was this a grace login?
643
+				if ($pwdGraceAuthNLimit 
644 644
 					&& (count($pwdGraceAuthNLimit) > 0)
645 645
 					&&($pwdGraceUseTimeCount < intval($pwdGraceAuthNLimit[0]))) { //at least one more grace login available?
646 646
 					$this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
@@ -653,24 +653,24 @@  discard block
 block discarded – undo
653 653
 				exit();
654 654
 			}
655 655
 			//handle pwdReset attribute
656
-			if($pwdReset && (count($pwdReset) > 0) && $pwdReset[0] === 'TRUE') { //user must change his password
656
+			if ($pwdReset && (count($pwdReset) > 0) && $pwdReset[0] === 'TRUE') { //user must change his password
657 657
 				$this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
658 658
 				header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
659 659
 				'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid)));
660 660
 				exit();
661 661
 			}
662 662
 			//handle password expiry warning
663
-			if($pwdChangedTime && (count($pwdChangedTime) > 0)) {
664
-				if($pwdMaxAge && (count($pwdMaxAge) > 0)
663
+			if ($pwdChangedTime && (count($pwdChangedTime) > 0)) {
664
+				if ($pwdMaxAge && (count($pwdMaxAge) > 0)
665 665
 					&& $pwdExpireWarning && (count($pwdExpireWarning) > 0)) {
666 666
 					$pwdMaxAgeInt = intval($pwdMaxAge[0]);
667 667
 					$pwdExpireWarningInt = intval($pwdExpireWarning[0]);
668
-					if($pwdMaxAgeInt > 0 && $pwdExpireWarningInt > 0){
668
+					if ($pwdMaxAgeInt > 0 && $pwdExpireWarningInt > 0) {
669 669
 						$pwdChangedTimeDt = \DateTime::createFromFormat('YmdHisZ', $pwdChangedTime[0]);
670 670
 						$pwdChangedTimeDt->add(new \DateInterval('PT'.$pwdMaxAgeInt.'S'));
671 671
 						$currentDateTime = new \DateTime();
672 672
 						$secondsToExpiry = $pwdChangedTimeDt->getTimestamp() - $currentDateTime->getTimestamp();
673
-						if($secondsToExpiry <= $pwdExpireWarningInt) {
673
+						if ($secondsToExpiry <= $pwdExpireWarningInt) {
674 674
 							//remove last password expiry warning if any
675 675
 							$notification = $this->notificationManager->createNotification();
676 676
 							$notification->setApp('user_ldap')
Please login to merge, or discard this patch.
apps/user_ldap/lib/Jobs/Sync.php 2 patches
Spacing   +23 added lines, -23 removed lines patch added patch discarded remove patch
@@ -97,7 +97,7 @@  discard block
 block discarded – undo
97 97
 			$pagingSize = $config->getAppValue('user_ldap', $configKey, $minPagingSize);
98 98
 			$minPagingSize = $minPagingSize === null ? $pagingSize : min($minPagingSize, $pagingSize);
99 99
 		}
100
-		return (int)$minPagingSize;
100
+		return (int) $minPagingSize;
101 101
 	}
102 102
 
103 103
 	/**
@@ -108,20 +108,20 @@  discard block
 block discarded – undo
108 108
 
109 109
 		$isBackgroundJobModeAjax = $this->c->getConfig()
110 110
 				->getAppValue('core', 'backgroundjobs_mode', 'ajax') === 'ajax';
111
-		if($isBackgroundJobModeAjax) {
111
+		if ($isBackgroundJobModeAjax) {
112 112
 			return;
113 113
 		}
114 114
 
115 115
 		$cycleData = $this->getCycle();
116
-		if($cycleData === null) {
116
+		if ($cycleData === null) {
117 117
 			$cycleData = $this->determineNextCycle();
118
-			if($cycleData === null) {
118
+			if ($cycleData === null) {
119 119
 				$this->updateInterval();
120 120
 				return;
121 121
 			}
122 122
 		}
123 123
 
124
-		if(!$this->qualifiesToRun($cycleData)) {
124
+		if (!$this->qualifiesToRun($cycleData)) {
125 125
 			$this->updateInterval();
126 126
 			return;
127 127
 		}
@@ -150,7 +150,7 @@  discard block
 block discarded – undo
150 150
 
151 151
 		$filter = $access->combineFilterWithAnd(array(
152 152
 			$access->connection->ldapUserFilter,
153
-			$access->connection->ldapUserDisplayName . '=*',
153
+			$access->connection->ldapUserDisplayName.'=*',
154 154
 			$access->getFilterPartForUserSearch('')
155 155
 		));
156 156
 		$results = $access->fetchListOfUsers(
@@ -161,7 +161,7 @@  discard block
 block discarded – undo
161 161
 			true
162 162
 		);
163 163
 
164
-		if($connection->ldapPagingSize === 0) {
164
+		if ($connection->ldapPagingSize === 0) {
165 165
 			return true;
166 166
 		}
167 167
 		return count($results) !== $connection->ldapPagingSize;
@@ -175,17 +175,17 @@  discard block
 block discarded – undo
175 175
 	 */
176 176
 	public function getCycle() {
177 177
 		$prefixes = $this->ldapHelper->getServerConfigurationPrefixes(true);
178
-		if(count($prefixes) === 0) {
178
+		if (count($prefixes) === 0) {
179 179
 			return null;
180 180
 		}
181 181
 
182 182
 		$config = $this->c->getConfig();
183 183
 		$cycleData = [
184 184
 			'prefix' => $config->getAppValue('user_ldap', 'background_sync_prefix', null),
185
-			'offset' => (int)$config->getAppValue('user_ldap', 'background_sync_offset', 0),
185
+			'offset' => (int) $config->getAppValue('user_ldap', 'background_sync_offset', 0),
186 186
 		];
187 187
 
188
-		if(
188
+		if (
189 189
 			$cycleData['prefix'] !== null
190 190
 			&& in_array($cycleData['prefix'], $prefixes)
191 191
 		) {
@@ -215,14 +215,14 @@  discard block
 block discarded – undo
215 215
 	 */
216 216
 	public function determineNextCycle(array $cycleData = null) {
217 217
 		$prefixes = $this->ldapHelper->getServerConfigurationPrefixes(true);
218
-		if(count($prefixes) === 0) {
218
+		if (count($prefixes) === 0) {
219 219
 			return null;
220 220
 		}
221 221
 
222 222
 		// get the next prefix in line and remember it
223 223
 		$oldPrefix = $cycleData === null ? null : $cycleData['prefix'];
224 224
 		$prefix = $this->getNextPrefix($oldPrefix);
225
-		if($prefix === null) {
225
+		if ($prefix === null) {
226 226
 			return null;
227 227
 		}
228 228
 		$cycleData['prefix'] = $prefix;
@@ -241,8 +241,8 @@  discard block
 block discarded – undo
241 241
 	 */
242 242
 	protected function qualifiesToRun($cycleData) {
243 243
 		$config = $this->c->getConfig();
244
-		$lastChange = $config->getAppValue('user_ldap', $cycleData['prefix'] . '_lastChange', 0);
245
-		if((time() - $lastChange) > 60 * 30) {
244
+		$lastChange = $config->getAppValue('user_ldap', $cycleData['prefix'].'_lastChange', 0);
245
+		if ((time() - $lastChange) > 60 * 30) {
246 246
 			return true;
247 247
 		}
248 248
 		return false;
@@ -255,7 +255,7 @@  discard block
 block discarded – undo
255 255
 	 */
256 256
 	protected function increaseOffset($cycleData) {
257 257
 		$ldapConfig = new Configuration($cycleData['prefix']);
258
-		$cycleData['offset'] += (int)$ldapConfig->ldapPagingSize;
258
+		$cycleData['offset'] += (int) $ldapConfig->ldapPagingSize;
259 259
 		$this->setCycle($cycleData);
260 260
 	}
261 261
 
@@ -268,17 +268,17 @@  discard block
 block discarded – undo
268 268
 	protected function getNextPrefix($lastPrefix) {
269 269
 		$prefixes = $this->ldapHelper->getServerConfigurationPrefixes(true);
270 270
 		$noOfPrefixes = count($prefixes);
271
-		if($noOfPrefixes === 0) {
271
+		if ($noOfPrefixes === 0) {
272 272
 			return null;
273 273
 		}
274 274
 		$i = $lastPrefix === null ? false : array_search($lastPrefix, $prefixes, true);
275
-		if($i === false) {
275
+		if ($i === false) {
276 276
 			$i = -1;
277 277
 		} else {
278 278
 			$i++;
279 279
 		}
280 280
 
281
-		if(!isset($prefixes[$i])) {
281
+		if (!isset($prefixes[$i])) {
282 282
 			$i = 0;
283 283
 		}
284 284
 		return $prefixes[$i];
@@ -290,25 +290,25 @@  discard block
 block discarded – undo
290 290
 	 * @param array $argument
291 291
 	 */
292 292
 	public function setArgument($argument) {
293
-		if(isset($argument['c'])) {
293
+		if (isset($argument['c'])) {
294 294
 			$this->c = $argument['c'];
295 295
 		} else {
296 296
 			$this->c = \OC::$server;
297 297
 		}
298 298
 
299
-		if(isset($argument['helper'])) {
299
+		if (isset($argument['helper'])) {
300 300
 			$this->ldapHelper = $argument['helper'];
301 301
 		} else {
302 302
 			$this->ldapHelper = new Helper($this->c->getConfig());
303 303
 		}
304 304
 
305
-		if(isset($argument['ldapWrapper'])) {
305
+		if (isset($argument['ldapWrapper'])) {
306 306
 			$this->ldap = $argument['ldapWrapper'];
307 307
 		} else {
308 308
 			$this->ldap = new LDAP();
309 309
 		}
310 310
 
311
-		if(isset($argument['userManager'])) {
311
+		if (isset($argument['userManager'])) {
312 312
 			$this->userManager = $argument['userManager'];
313 313
 		} else {
314 314
 			$this->userManager = new Manager(
@@ -323,7 +323,7 @@  discard block
 block discarded – undo
323 323
 			);
324 324
 		}
325 325
 
326
-		if(isset($argument['mapper'])) {
326
+		if (isset($argument['mapper'])) {
327 327
 			$this->mapper = $argument['mapper'];
328 328
 		} else {
329 329
 			$this->mapper = new UserMapping($this->c->getDatabaseConnection());
Please login to merge, or discard this patch.
Indentation   +289 added lines, -289 removed lines patch added patch discarded remove patch
@@ -38,293 +38,293 @@
 block discarded – undo
38 38
 use OCP\IServerContainer;
39 39
 
40 40
 class Sync extends TimedJob {
41
-	const MAX_INTERVAL = 12 * 60 * 60; // 12h
42
-	const MIN_INTERVAL = 30 * 60; // 30min
43
-	/** @var IServerContainer */
44
-	protected $c;
45
-	/** @var  Helper */
46
-	protected $ldapHelper;
47
-	/** @var  LDAP */
48
-	protected $ldap;
49
-	/** @var  Manager */
50
-	protected $userManager;
51
-	/** @var UserMapping */
52
-	protected $mapper;
53
-
54
-	public function __construct() {
55
-		$this->setInterval(
56
-			\OC::$server->getConfig()->getAppValue(
57
-				'user_ldap',
58
-				'background_sync_interval',
59
-				self::MIN_INTERVAL
60
-			)
61
-		);
62
-	}
63
-
64
-	/**
65
-	 * updates the interval
66
-	 *
67
-	 * the idea is to adjust the interval depending on the amount of known users
68
-	 * and the attempt to update each user one day. At most it would run every
69
-	 * 30 minutes, and at least every 12 hours.
70
-	 */
71
-	public function updateInterval() {
72
-		$minPagingSize = $this->getMinPagingSize();
73
-		$mappedUsers = $this->mapper->count();
74
-
75
-		$runsPerDay = ($minPagingSize === 0 || $mappedUsers === 0) ? self::MAX_INTERVAL
76
-			: $mappedUsers / $minPagingSize;
77
-		$interval = floor(24 * 60 * 60 / $runsPerDay);
78
-		$interval = min(max($interval, self::MIN_INTERVAL), self::MAX_INTERVAL);
79
-
80
-		$this->c->getConfig()->setAppValue('user_ldap', 'background_sync_interval', $interval);
81
-	}
82
-
83
-	/**
84
-	 * returns the smallest configured paging size
85
-	 * @return int
86
-	 */
87
-	protected function getMinPagingSize() {
88
-		$config = $this->c->getConfig();
89
-		$configKeys = $config->getAppKeys('user_ldap');
90
-		$configKeys = array_filter($configKeys, function($key) {
91
-			return strpos($key, 'ldap_paging_size') !== false;
92
-		});
93
-		$minPagingSize = null;
94
-		foreach ($configKeys as $configKey) {
95
-			$pagingSize = $config->getAppValue('user_ldap', $configKey, $minPagingSize);
96
-			$minPagingSize = $minPagingSize === null ? $pagingSize : min($minPagingSize, $pagingSize);
97
-		}
98
-		return (int)$minPagingSize;
99
-	}
100
-
101
-	/**
102
-	 * @param array $argument
103
-	 */
104
-	protected function run($argument) {
105
-		$this->setArgument($argument);
106
-
107
-		$isBackgroundJobModeAjax = $this->c->getConfig()
108
-				->getAppValue('core', 'backgroundjobs_mode', 'ajax') === 'ajax';
109
-		if($isBackgroundJobModeAjax) {
110
-			return;
111
-		}
112
-
113
-		$cycleData = $this->getCycle();
114
-		if($cycleData === null) {
115
-			$cycleData = $this->determineNextCycle();
116
-			if($cycleData === null) {
117
-				$this->updateInterval();
118
-				return;
119
-			}
120
-		}
121
-
122
-		if(!$this->qualifiesToRun($cycleData)) {
123
-			$this->updateInterval();
124
-			return;
125
-		}
126
-
127
-		try {
128
-			$expectMoreResults = $this->runCycle($cycleData);
129
-			if ($expectMoreResults) {
130
-				$this->increaseOffset($cycleData);
131
-			} else {
132
-				$this->determineNextCycle();
133
-			}
134
-			$this->updateInterval();
135
-		} catch (ServerNotAvailableException $e) {
136
-			$this->determineNextCycle();
137
-		}
138
-	}
139
-
140
-	/**
141
-	 * @param array $cycleData
142
-	 * @return bool whether more results are expected from the same configuration
143
-	 */
144
-	public function runCycle($cycleData) {
145
-		$connection = new Connection($this->ldap, $cycleData['prefix']);
146
-		$access = new Access($connection, $this->ldap, $this->userManager, $this->ldapHelper, $this->c);
147
-		$access->setUserMapper($this->mapper);
148
-
149
-		$filter = $access->combineFilterWithAnd(array(
150
-			$access->connection->ldapUserFilter,
151
-			$access->connection->ldapUserDisplayName . '=*',
152
-			$access->getFilterPartForUserSearch('')
153
-		));
154
-		$results = $access->fetchListOfUsers(
155
-			$filter,
156
-			$access->userManager->getAttributes(),
157
-			$connection->ldapPagingSize,
158
-			$cycleData['offset'],
159
-			true
160
-		);
161
-
162
-		if($connection->ldapPagingSize === 0) {
163
-			return true;
164
-		}
165
-		return count($results) !== $connection->ldapPagingSize;
166
-	}
167
-
168
-	/**
169
-	 * returns the info about the current cycle that should be run, if any,
170
-	 * otherwise null
171
-	 *
172
-	 * @return array|null
173
-	 */
174
-	public function getCycle() {
175
-		$prefixes = $this->ldapHelper->getServerConfigurationPrefixes(true);
176
-		if(count($prefixes) === 0) {
177
-			return null;
178
-		}
179
-
180
-		$config = $this->c->getConfig();
181
-		$cycleData = [
182
-			'prefix' => $config->getAppValue('user_ldap', 'background_sync_prefix', null),
183
-			'offset' => (int)$config->getAppValue('user_ldap', 'background_sync_offset', 0),
184
-		];
185
-
186
-		if(
187
-			$cycleData['prefix'] !== null
188
-			&& in_array($cycleData['prefix'], $prefixes)
189
-		) {
190
-			return $cycleData;
191
-		}
192
-
193
-		return null;
194
-	}
195
-
196
-	/**
197
-	 * Save the provided cycle information in the DB
198
-	 *
199
-	 * @param array $cycleData
200
-	 */
201
-	public function setCycle(array $cycleData) {
202
-		$config = $this->c->getConfig();
203
-		$config->setAppValue('user_ldap', 'background_sync_prefix', $cycleData['prefix']);
204
-		$config->setAppValue('user_ldap', 'background_sync_offset', $cycleData['offset']);
205
-	}
206
-
207
-	/**
208
-	 * returns data about the next cycle that should run, if any, otherwise
209
-	 * null. It also always goes for the next LDAP configuration!
210
-	 *
211
-	 * @param array|null $cycleData the old cycle
212
-	 * @return array|null
213
-	 */
214
-	public function determineNextCycle(array $cycleData = null) {
215
-		$prefixes = $this->ldapHelper->getServerConfigurationPrefixes(true);
216
-		if(count($prefixes) === 0) {
217
-			return null;
218
-		}
219
-
220
-		// get the next prefix in line and remember it
221
-		$oldPrefix = $cycleData === null ? null : $cycleData['prefix'];
222
-		$prefix = $this->getNextPrefix($oldPrefix);
223
-		if($prefix === null) {
224
-			return null;
225
-		}
226
-		$cycleData['prefix'] = $prefix;
227
-		$cycleData['offset'] = 0;
228
-		$this->setCycle(['prefix' => $prefix, 'offset' => 0]);
229
-
230
-		return $cycleData;
231
-	}
232
-
233
-	/**
234
-	 * Checks whether the provided cycle should be run. Currently only the
235
-	 * last configuration change goes into account (at least one hour).
236
-	 *
237
-	 * @param $cycleData
238
-	 * @return bool
239
-	 */
240
-	protected function qualifiesToRun($cycleData) {
241
-		$config = $this->c->getConfig();
242
-		$lastChange = $config->getAppValue('user_ldap', $cycleData['prefix'] . '_lastChange', 0);
243
-		if((time() - $lastChange) > 60 * 30) {
244
-			return true;
245
-		}
246
-		return false;
247
-	}
248
-
249
-	/**
250
-	 * increases the offset of the current cycle for the next run
251
-	 *
252
-	 * @param $cycleData
253
-	 */
254
-	protected function increaseOffset($cycleData) {
255
-		$ldapConfig = new Configuration($cycleData['prefix']);
256
-		$cycleData['offset'] += (int)$ldapConfig->ldapPagingSize;
257
-		$this->setCycle($cycleData);
258
-	}
259
-
260
-	/**
261
-	 * determines the next configuration prefix based on the last one (if any)
262
-	 *
263
-	 * @param string|null $lastPrefix
264
-	 * @return string|null
265
-	 */
266
-	protected function getNextPrefix($lastPrefix) {
267
-		$prefixes = $this->ldapHelper->getServerConfigurationPrefixes(true);
268
-		$noOfPrefixes = count($prefixes);
269
-		if($noOfPrefixes === 0) {
270
-			return null;
271
-		}
272
-		$i = $lastPrefix === null ? false : array_search($lastPrefix, $prefixes, true);
273
-		if($i === false) {
274
-			$i = -1;
275
-		} else {
276
-			$i++;
277
-		}
278
-
279
-		if(!isset($prefixes[$i])) {
280
-			$i = 0;
281
-		}
282
-		return $prefixes[$i];
283
-	}
284
-
285
-	/**
286
-	 * "fixes" DI
287
-	 *
288
-	 * @param array $argument
289
-	 */
290
-	public function setArgument($argument) {
291
-		if(isset($argument['c'])) {
292
-			$this->c = $argument['c'];
293
-		} else {
294
-			$this->c = \OC::$server;
295
-		}
296
-
297
-		if(isset($argument['helper'])) {
298
-			$this->ldapHelper = $argument['helper'];
299
-		} else {
300
-			$this->ldapHelper = new Helper($this->c->getConfig());
301
-		}
302
-
303
-		if(isset($argument['ldapWrapper'])) {
304
-			$this->ldap = $argument['ldapWrapper'];
305
-		} else {
306
-			$this->ldap = new LDAP();
307
-		}
308
-
309
-		if(isset($argument['userManager'])) {
310
-			$this->userManager = $argument['userManager'];
311
-		} else {
312
-			$this->userManager = new Manager(
313
-				$this->c->getConfig(),
314
-				new FilesystemHelper(),
315
-				new LogWrapper(),
316
-				$this->c->getAvatarManager(),
317
-				new Image(),
318
-				$this->c->getDatabaseConnection(),
319
-				$this->c->getUserManager(),
320
-				$this->c->getNotificationManager()
321
-			);
322
-		}
323
-
324
-		if(isset($argument['mapper'])) {
325
-			$this->mapper = $argument['mapper'];
326
-		} else {
327
-			$this->mapper = new UserMapping($this->c->getDatabaseConnection());
328
-		}
329
-	}
41
+    const MAX_INTERVAL = 12 * 60 * 60; // 12h
42
+    const MIN_INTERVAL = 30 * 60; // 30min
43
+    /** @var IServerContainer */
44
+    protected $c;
45
+    /** @var  Helper */
46
+    protected $ldapHelper;
47
+    /** @var  LDAP */
48
+    protected $ldap;
49
+    /** @var  Manager */
50
+    protected $userManager;
51
+    /** @var UserMapping */
52
+    protected $mapper;
53
+
54
+    public function __construct() {
55
+        $this->setInterval(
56
+            \OC::$server->getConfig()->getAppValue(
57
+                'user_ldap',
58
+                'background_sync_interval',
59
+                self::MIN_INTERVAL
60
+            )
61
+        );
62
+    }
63
+
64
+    /**
65
+     * updates the interval
66
+     *
67
+     * the idea is to adjust the interval depending on the amount of known users
68
+     * and the attempt to update each user one day. At most it would run every
69
+     * 30 minutes, and at least every 12 hours.
70
+     */
71
+    public function updateInterval() {
72
+        $minPagingSize = $this->getMinPagingSize();
73
+        $mappedUsers = $this->mapper->count();
74
+
75
+        $runsPerDay = ($minPagingSize === 0 || $mappedUsers === 0) ? self::MAX_INTERVAL
76
+            : $mappedUsers / $minPagingSize;
77
+        $interval = floor(24 * 60 * 60 / $runsPerDay);
78
+        $interval = min(max($interval, self::MIN_INTERVAL), self::MAX_INTERVAL);
79
+
80
+        $this->c->getConfig()->setAppValue('user_ldap', 'background_sync_interval', $interval);
81
+    }
82
+
83
+    /**
84
+     * returns the smallest configured paging size
85
+     * @return int
86
+     */
87
+    protected function getMinPagingSize() {
88
+        $config = $this->c->getConfig();
89
+        $configKeys = $config->getAppKeys('user_ldap');
90
+        $configKeys = array_filter($configKeys, function($key) {
91
+            return strpos($key, 'ldap_paging_size') !== false;
92
+        });
93
+        $minPagingSize = null;
94
+        foreach ($configKeys as $configKey) {
95
+            $pagingSize = $config->getAppValue('user_ldap', $configKey, $minPagingSize);
96
+            $minPagingSize = $minPagingSize === null ? $pagingSize : min($minPagingSize, $pagingSize);
97
+        }
98
+        return (int)$minPagingSize;
99
+    }
100
+
101
+    /**
102
+     * @param array $argument
103
+     */
104
+    protected function run($argument) {
105
+        $this->setArgument($argument);
106
+
107
+        $isBackgroundJobModeAjax = $this->c->getConfig()
108
+                ->getAppValue('core', 'backgroundjobs_mode', 'ajax') === 'ajax';
109
+        if($isBackgroundJobModeAjax) {
110
+            return;
111
+        }
112
+
113
+        $cycleData = $this->getCycle();
114
+        if($cycleData === null) {
115
+            $cycleData = $this->determineNextCycle();
116
+            if($cycleData === null) {
117
+                $this->updateInterval();
118
+                return;
119
+            }
120
+        }
121
+
122
+        if(!$this->qualifiesToRun($cycleData)) {
123
+            $this->updateInterval();
124
+            return;
125
+        }
126
+
127
+        try {
128
+            $expectMoreResults = $this->runCycle($cycleData);
129
+            if ($expectMoreResults) {
130
+                $this->increaseOffset($cycleData);
131
+            } else {
132
+                $this->determineNextCycle();
133
+            }
134
+            $this->updateInterval();
135
+        } catch (ServerNotAvailableException $e) {
136
+            $this->determineNextCycle();
137
+        }
138
+    }
139
+
140
+    /**
141
+     * @param array $cycleData
142
+     * @return bool whether more results are expected from the same configuration
143
+     */
144
+    public function runCycle($cycleData) {
145
+        $connection = new Connection($this->ldap, $cycleData['prefix']);
146
+        $access = new Access($connection, $this->ldap, $this->userManager, $this->ldapHelper, $this->c);
147
+        $access->setUserMapper($this->mapper);
148
+
149
+        $filter = $access->combineFilterWithAnd(array(
150
+            $access->connection->ldapUserFilter,
151
+            $access->connection->ldapUserDisplayName . '=*',
152
+            $access->getFilterPartForUserSearch('')
153
+        ));
154
+        $results = $access->fetchListOfUsers(
155
+            $filter,
156
+            $access->userManager->getAttributes(),
157
+            $connection->ldapPagingSize,
158
+            $cycleData['offset'],
159
+            true
160
+        );
161
+
162
+        if($connection->ldapPagingSize === 0) {
163
+            return true;
164
+        }
165
+        return count($results) !== $connection->ldapPagingSize;
166
+    }
167
+
168
+    /**
169
+     * returns the info about the current cycle that should be run, if any,
170
+     * otherwise null
171
+     *
172
+     * @return array|null
173
+     */
174
+    public function getCycle() {
175
+        $prefixes = $this->ldapHelper->getServerConfigurationPrefixes(true);
176
+        if(count($prefixes) === 0) {
177
+            return null;
178
+        }
179
+
180
+        $config = $this->c->getConfig();
181
+        $cycleData = [
182
+            'prefix' => $config->getAppValue('user_ldap', 'background_sync_prefix', null),
183
+            'offset' => (int)$config->getAppValue('user_ldap', 'background_sync_offset', 0),
184
+        ];
185
+
186
+        if(
187
+            $cycleData['prefix'] !== null
188
+            && in_array($cycleData['prefix'], $prefixes)
189
+        ) {
190
+            return $cycleData;
191
+        }
192
+
193
+        return null;
194
+    }
195
+
196
+    /**
197
+     * Save the provided cycle information in the DB
198
+     *
199
+     * @param array $cycleData
200
+     */
201
+    public function setCycle(array $cycleData) {
202
+        $config = $this->c->getConfig();
203
+        $config->setAppValue('user_ldap', 'background_sync_prefix', $cycleData['prefix']);
204
+        $config->setAppValue('user_ldap', 'background_sync_offset', $cycleData['offset']);
205
+    }
206
+
207
+    /**
208
+     * returns data about the next cycle that should run, if any, otherwise
209
+     * null. It also always goes for the next LDAP configuration!
210
+     *
211
+     * @param array|null $cycleData the old cycle
212
+     * @return array|null
213
+     */
214
+    public function determineNextCycle(array $cycleData = null) {
215
+        $prefixes = $this->ldapHelper->getServerConfigurationPrefixes(true);
216
+        if(count($prefixes) === 0) {
217
+            return null;
218
+        }
219
+
220
+        // get the next prefix in line and remember it
221
+        $oldPrefix = $cycleData === null ? null : $cycleData['prefix'];
222
+        $prefix = $this->getNextPrefix($oldPrefix);
223
+        if($prefix === null) {
224
+            return null;
225
+        }
226
+        $cycleData['prefix'] = $prefix;
227
+        $cycleData['offset'] = 0;
228
+        $this->setCycle(['prefix' => $prefix, 'offset' => 0]);
229
+
230
+        return $cycleData;
231
+    }
232
+
233
+    /**
234
+     * Checks whether the provided cycle should be run. Currently only the
235
+     * last configuration change goes into account (at least one hour).
236
+     *
237
+     * @param $cycleData
238
+     * @return bool
239
+     */
240
+    protected function qualifiesToRun($cycleData) {
241
+        $config = $this->c->getConfig();
242
+        $lastChange = $config->getAppValue('user_ldap', $cycleData['prefix'] . '_lastChange', 0);
243
+        if((time() - $lastChange) > 60 * 30) {
244
+            return true;
245
+        }
246
+        return false;
247
+    }
248
+
249
+    /**
250
+     * increases the offset of the current cycle for the next run
251
+     *
252
+     * @param $cycleData
253
+     */
254
+    protected function increaseOffset($cycleData) {
255
+        $ldapConfig = new Configuration($cycleData['prefix']);
256
+        $cycleData['offset'] += (int)$ldapConfig->ldapPagingSize;
257
+        $this->setCycle($cycleData);
258
+    }
259
+
260
+    /**
261
+     * determines the next configuration prefix based on the last one (if any)
262
+     *
263
+     * @param string|null $lastPrefix
264
+     * @return string|null
265
+     */
266
+    protected function getNextPrefix($lastPrefix) {
267
+        $prefixes = $this->ldapHelper->getServerConfigurationPrefixes(true);
268
+        $noOfPrefixes = count($prefixes);
269
+        if($noOfPrefixes === 0) {
270
+            return null;
271
+        }
272
+        $i = $lastPrefix === null ? false : array_search($lastPrefix, $prefixes, true);
273
+        if($i === false) {
274
+            $i = -1;
275
+        } else {
276
+            $i++;
277
+        }
278
+
279
+        if(!isset($prefixes[$i])) {
280
+            $i = 0;
281
+        }
282
+        return $prefixes[$i];
283
+    }
284
+
285
+    /**
286
+     * "fixes" DI
287
+     *
288
+     * @param array $argument
289
+     */
290
+    public function setArgument($argument) {
291
+        if(isset($argument['c'])) {
292
+            $this->c = $argument['c'];
293
+        } else {
294
+            $this->c = \OC::$server;
295
+        }
296
+
297
+        if(isset($argument['helper'])) {
298
+            $this->ldapHelper = $argument['helper'];
299
+        } else {
300
+            $this->ldapHelper = new Helper($this->c->getConfig());
301
+        }
302
+
303
+        if(isset($argument['ldapWrapper'])) {
304
+            $this->ldap = $argument['ldapWrapper'];
305
+        } else {
306
+            $this->ldap = new LDAP();
307
+        }
308
+
309
+        if(isset($argument['userManager'])) {
310
+            $this->userManager = $argument['userManager'];
311
+        } else {
312
+            $this->userManager = new Manager(
313
+                $this->c->getConfig(),
314
+                new FilesystemHelper(),
315
+                new LogWrapper(),
316
+                $this->c->getAvatarManager(),
317
+                new Image(),
318
+                $this->c->getDatabaseConnection(),
319
+                $this->c->getUserManager(),
320
+                $this->c->getNotificationManager()
321
+            );
322
+        }
323
+
324
+        if(isset($argument['mapper'])) {
325
+            $this->mapper = $argument['mapper'];
326
+        } else {
327
+            $this->mapper = new UserMapping($this->c->getDatabaseConnection());
328
+        }
329
+    }
330 330
 }
Please login to merge, or discard this patch.
apps/user_ldap/lib/Jobs/UpdateGroups.php 1 patch
Indentation   +164 added lines, -164 removed lines patch added patch discarded remove patch
@@ -41,183 +41,183 @@
 block discarded – undo
41 41
 use OCA\User_LDAP\User\Manager;
42 42
 
43 43
 class UpdateGroups extends \OC\BackgroundJob\TimedJob {
44
-	static private $groupsFromDB;
45
-
46
-	static private $groupBE;
47
-
48
-	public function __construct(){
49
-		$this->interval = self::getRefreshInterval();
50
-	}
51
-
52
-	/**
53
-	 * @param mixed $argument
54
-	 */
55
-	public function run($argument){
56
-		self::updateGroups();
57
-	}
58
-
59
-	static public function updateGroups() {
60
-		\OCP\Util::writeLog('user_ldap', 'Run background job "updateGroups"', \OCP\Util::DEBUG);
61
-
62
-		$knownGroups = array_keys(self::getKnownGroups());
63
-		$actualGroups = self::getGroupBE()->getGroups();
64
-
65
-		if(empty($actualGroups) && empty($knownGroups)) {
66
-			\OCP\Util::writeLog('user_ldap',
67
-				'bgJ "updateGroups" – groups do not seem to be configured properly, aborting.',
68
-				\OCP\Util::INFO);
69
-			return;
70
-		}
71
-
72
-		self::handleKnownGroups(array_intersect($actualGroups, $knownGroups));
73
-		self::handleCreatedGroups(array_diff($actualGroups, $knownGroups));
74
-		self::handleRemovedGroups(array_diff($knownGroups, $actualGroups));
75
-
76
-		\OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – Finished.', \OCP\Util::DEBUG);
77
-	}
78
-
79
-	/**
80
-	 * @return int
81
-	 */
82
-	static private function getRefreshInterval() {
83
-		//defaults to every hour
84
-		return \OCP\Config::getAppValue('user_ldap', 'bgjRefreshInterval', 3600);
85
-	}
86
-
87
-	/**
88
-	 * @param string[] $groups
89
-	 */
90
-	static private function handleKnownGroups($groups) {
91
-		\OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – Dealing with known Groups.', \OCP\Util::DEBUG);
92
-		$query = \OCP\DB::prepare('
44
+    static private $groupsFromDB;
45
+
46
+    static private $groupBE;
47
+
48
+    public function __construct(){
49
+        $this->interval = self::getRefreshInterval();
50
+    }
51
+
52
+    /**
53
+     * @param mixed $argument
54
+     */
55
+    public function run($argument){
56
+        self::updateGroups();
57
+    }
58
+
59
+    static public function updateGroups() {
60
+        \OCP\Util::writeLog('user_ldap', 'Run background job "updateGroups"', \OCP\Util::DEBUG);
61
+
62
+        $knownGroups = array_keys(self::getKnownGroups());
63
+        $actualGroups = self::getGroupBE()->getGroups();
64
+
65
+        if(empty($actualGroups) && empty($knownGroups)) {
66
+            \OCP\Util::writeLog('user_ldap',
67
+                'bgJ "updateGroups" – groups do not seem to be configured properly, aborting.',
68
+                \OCP\Util::INFO);
69
+            return;
70
+        }
71
+
72
+        self::handleKnownGroups(array_intersect($actualGroups, $knownGroups));
73
+        self::handleCreatedGroups(array_diff($actualGroups, $knownGroups));
74
+        self::handleRemovedGroups(array_diff($knownGroups, $actualGroups));
75
+
76
+        \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – Finished.', \OCP\Util::DEBUG);
77
+    }
78
+
79
+    /**
80
+     * @return int
81
+     */
82
+    static private function getRefreshInterval() {
83
+        //defaults to every hour
84
+        return \OCP\Config::getAppValue('user_ldap', 'bgjRefreshInterval', 3600);
85
+    }
86
+
87
+    /**
88
+     * @param string[] $groups
89
+     */
90
+    static private function handleKnownGroups($groups) {
91
+        \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – Dealing with known Groups.', \OCP\Util::DEBUG);
92
+        $query = \OCP\DB::prepare('
93 93
 			UPDATE `*PREFIX*ldap_group_members`
94 94
 			SET `owncloudusers` = ?
95 95
 			WHERE `owncloudname` = ?
96 96
 		');
97
-		foreach($groups as $group) {
98
-			//we assume, that self::$groupsFromDB has been retrieved already
99
-			$knownUsers = unserialize(self::$groupsFromDB[$group]['owncloudusers']);
100
-			$actualUsers = self::getGroupBE()->usersInGroup($group);
101
-			$hasChanged = false;
102
-			foreach(array_diff($knownUsers, $actualUsers) as $removedUser) {
103
-				\OCP\Util::emitHook('OC_User', 'post_removeFromGroup', array('uid' => $removedUser, 'gid' => $group));
104
-				\OCP\Util::writeLog('user_ldap',
105
-				'bgJ "updateGroups" – "'.$removedUser.'" removed from "'.$group.'".',
106
-				\OCP\Util::INFO);
107
-				$hasChanged = true;
108
-			}
109
-			foreach(array_diff($actualUsers, $knownUsers) as $addedUser) {
110
-				\OCP\Util::emitHook('OC_User', 'post_addToGroup', array('uid' => $addedUser, 'gid' => $group));
111
-				\OCP\Util::writeLog('user_ldap',
112
-				'bgJ "updateGroups" – "'.$addedUser.'" added to "'.$group.'".',
113
-				\OCP\Util::INFO);
114
-				$hasChanged = true;
115
-			}
116
-			if($hasChanged) {
117
-				$query->execute(array(serialize($actualUsers), $group));
118
-			}
119
-		}
120
-		\OCP\Util::writeLog('user_ldap',
121
-			'bgJ "updateGroups" – FINISHED dealing with known Groups.',
122
-			\OCP\Util::DEBUG);
123
-	}
124
-
125
-	/**
126
-	 * @param string[] $createdGroups
127
-	 */
128
-	static private function handleCreatedGroups($createdGroups) {
129
-		\OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – dealing with created Groups.', \OCP\Util::DEBUG);
130
-		$query = \OCP\DB::prepare('
97
+        foreach($groups as $group) {
98
+            //we assume, that self::$groupsFromDB has been retrieved already
99
+            $knownUsers = unserialize(self::$groupsFromDB[$group]['owncloudusers']);
100
+            $actualUsers = self::getGroupBE()->usersInGroup($group);
101
+            $hasChanged = false;
102
+            foreach(array_diff($knownUsers, $actualUsers) as $removedUser) {
103
+                \OCP\Util::emitHook('OC_User', 'post_removeFromGroup', array('uid' => $removedUser, 'gid' => $group));
104
+                \OCP\Util::writeLog('user_ldap',
105
+                'bgJ "updateGroups" – "'.$removedUser.'" removed from "'.$group.'".',
106
+                \OCP\Util::INFO);
107
+                $hasChanged = true;
108
+            }
109
+            foreach(array_diff($actualUsers, $knownUsers) as $addedUser) {
110
+                \OCP\Util::emitHook('OC_User', 'post_addToGroup', array('uid' => $addedUser, 'gid' => $group));
111
+                \OCP\Util::writeLog('user_ldap',
112
+                'bgJ "updateGroups" – "'.$addedUser.'" added to "'.$group.'".',
113
+                \OCP\Util::INFO);
114
+                $hasChanged = true;
115
+            }
116
+            if($hasChanged) {
117
+                $query->execute(array(serialize($actualUsers), $group));
118
+            }
119
+        }
120
+        \OCP\Util::writeLog('user_ldap',
121
+            'bgJ "updateGroups" – FINISHED dealing with known Groups.',
122
+            \OCP\Util::DEBUG);
123
+    }
124
+
125
+    /**
126
+     * @param string[] $createdGroups
127
+     */
128
+    static private function handleCreatedGroups($createdGroups) {
129
+        \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – dealing with created Groups.', \OCP\Util::DEBUG);
130
+        $query = \OCP\DB::prepare('
131 131
 			INSERT
132 132
 			INTO `*PREFIX*ldap_group_members` (`owncloudname`, `owncloudusers`)
133 133
 			VALUES (?, ?)
134 134
 		');
135
-		foreach($createdGroups as $createdGroup) {
136
-			\OCP\Util::writeLog('user_ldap',
137
-				'bgJ "updateGroups" – new group "'.$createdGroup.'" found.',
138
-				\OCP\Util::INFO);
139
-			$users = serialize(self::getGroupBE()->usersInGroup($createdGroup));
140
-			$query->execute(array($createdGroup, $users));
141
-		}
142
-		\OCP\Util::writeLog('user_ldap',
143
-			'bgJ "updateGroups" – FINISHED dealing with created Groups.',
144
-			\OCP\Util::DEBUG);
145
-	}
146
-
147
-	/**
148
-	 * @param string[] $removedGroups
149
-	 */
150
-	static private function handleRemovedGroups($removedGroups) {
151
-		\OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – dealing with removed groups.', \OCP\Util::DEBUG);
152
-		$query = \OCP\DB::prepare('
135
+        foreach($createdGroups as $createdGroup) {
136
+            \OCP\Util::writeLog('user_ldap',
137
+                'bgJ "updateGroups" – new group "'.$createdGroup.'" found.',
138
+                \OCP\Util::INFO);
139
+            $users = serialize(self::getGroupBE()->usersInGroup($createdGroup));
140
+            $query->execute(array($createdGroup, $users));
141
+        }
142
+        \OCP\Util::writeLog('user_ldap',
143
+            'bgJ "updateGroups" – FINISHED dealing with created Groups.',
144
+            \OCP\Util::DEBUG);
145
+    }
146
+
147
+    /**
148
+     * @param string[] $removedGroups
149
+     */
150
+    static private function handleRemovedGroups($removedGroups) {
151
+        \OCP\Util::writeLog('user_ldap', 'bgJ "updateGroups" – dealing with removed groups.', \OCP\Util::DEBUG);
152
+        $query = \OCP\DB::prepare('
153 153
 			DELETE
154 154
 			FROM `*PREFIX*ldap_group_members`
155 155
 			WHERE `owncloudname` = ?
156 156
 		');
157
-		foreach($removedGroups as $removedGroup) {
158
-			\OCP\Util::writeLog('user_ldap',
159
-				'bgJ "updateGroups" – group "'.$removedGroup.'" was removed.',
160
-				\OCP\Util::INFO);
161
-			$query->execute(array($removedGroup));
162
-		}
163
-		\OCP\Util::writeLog('user_ldap',
164
-			'bgJ "updateGroups" – FINISHED dealing with removed groups.',
165
-			\OCP\Util::DEBUG);
166
-	}
167
-
168
-	/**
169
-	 * @return \OCA\User_LDAP\Group_LDAP|\OCA\User_LDAP\Group_Proxy
170
-	 */
171
-	static private function getGroupBE() {
172
-		if(!is_null(self::$groupBE)) {
173
-			return self::$groupBE;
174
-		}
175
-		$helper = new Helper(\OC::$server->getConfig());
176
-		$configPrefixes = $helper->getServerConfigurationPrefixes(true);
177
-		$ldapWrapper = new LDAP();
178
-		if(count($configPrefixes) === 1) {
179
-			//avoid the proxy when there is only one LDAP server configured
180
-			$dbc = \OC::$server->getDatabaseConnection();
181
-			$userManager = new Manager(
182
-				\OC::$server->getConfig(),
183
-				new FilesystemHelper(),
184
-				new LogWrapper(),
185
-				\OC::$server->getAvatarManager(),
186
-				new \OCP\Image(),
187
-				$dbc,
188
-				\OC::$server->getUserManager(),
189
-				\OC::$server->getNotificationManager());
190
-			$connector = new Connection($ldapWrapper, $configPrefixes[0]);
191
-			$ldapAccess = new Access($connector, $ldapWrapper, $userManager, $helper, \OC::$server);
192
-			$groupMapper = new GroupMapping($dbc);
193
-			$userMapper  = new UserMapping($dbc);
194
-			$ldapAccess->setGroupMapper($groupMapper);
195
-			$ldapAccess->setUserMapper($userMapper);
196
-			self::$groupBE = new \OCA\User_LDAP\Group_LDAP($ldapAccess);
197
-		} else {
198
-			self::$groupBE = new \OCA\User_LDAP\Group_Proxy($configPrefixes, $ldapWrapper);
199
-		}
200
-
201
-		return self::$groupBE;
202
-	}
203
-
204
-	/**
205
-	 * @return array
206
-	 */
207
-	static private function getKnownGroups() {
208
-		if(is_array(self::$groupsFromDB)) {
209
-			return self::$groupsFromDB;
210
-		}
211
-		$query = \OCP\DB::prepare('
157
+        foreach($removedGroups as $removedGroup) {
158
+            \OCP\Util::writeLog('user_ldap',
159
+                'bgJ "updateGroups" – group "'.$removedGroup.'" was removed.',
160
+                \OCP\Util::INFO);
161
+            $query->execute(array($removedGroup));
162
+        }
163
+        \OCP\Util::writeLog('user_ldap',
164
+            'bgJ "updateGroups" – FINISHED dealing with removed groups.',
165
+            \OCP\Util::DEBUG);
166
+    }
167
+
168
+    /**
169
+     * @return \OCA\User_LDAP\Group_LDAP|\OCA\User_LDAP\Group_Proxy
170
+     */
171
+    static private function getGroupBE() {
172
+        if(!is_null(self::$groupBE)) {
173
+            return self::$groupBE;
174
+        }
175
+        $helper = new Helper(\OC::$server->getConfig());
176
+        $configPrefixes = $helper->getServerConfigurationPrefixes(true);
177
+        $ldapWrapper = new LDAP();
178
+        if(count($configPrefixes) === 1) {
179
+            //avoid the proxy when there is only one LDAP server configured
180
+            $dbc = \OC::$server->getDatabaseConnection();
181
+            $userManager = new Manager(
182
+                \OC::$server->getConfig(),
183
+                new FilesystemHelper(),
184
+                new LogWrapper(),
185
+                \OC::$server->getAvatarManager(),
186
+                new \OCP\Image(),
187
+                $dbc,
188
+                \OC::$server->getUserManager(),
189
+                \OC::$server->getNotificationManager());
190
+            $connector = new Connection($ldapWrapper, $configPrefixes[0]);
191
+            $ldapAccess = new Access($connector, $ldapWrapper, $userManager, $helper, \OC::$server);
192
+            $groupMapper = new GroupMapping($dbc);
193
+            $userMapper  = new UserMapping($dbc);
194
+            $ldapAccess->setGroupMapper($groupMapper);
195
+            $ldapAccess->setUserMapper($userMapper);
196
+            self::$groupBE = new \OCA\User_LDAP\Group_LDAP($ldapAccess);
197
+        } else {
198
+            self::$groupBE = new \OCA\User_LDAP\Group_Proxy($configPrefixes, $ldapWrapper);
199
+        }
200
+
201
+        return self::$groupBE;
202
+    }
203
+
204
+    /**
205
+     * @return array
206
+     */
207
+    static private function getKnownGroups() {
208
+        if(is_array(self::$groupsFromDB)) {
209
+            return self::$groupsFromDB;
210
+        }
211
+        $query = \OCP\DB::prepare('
212 212
 			SELECT `owncloudname`, `owncloudusers`
213 213
 			FROM `*PREFIX*ldap_group_members`
214 214
 		');
215
-		$result = $query->execute()->fetchAll();
216
-		self::$groupsFromDB = array();
217
-		foreach($result as $dataset) {
218
-			self::$groupsFromDB[$dataset['owncloudname']] = $dataset;
219
-		}
220
-
221
-		return self::$groupsFromDB;
222
-	}
215
+        $result = $query->execute()->fetchAll();
216
+        self::$groupsFromDB = array();
217
+        foreach($result as $dataset) {
218
+            self::$groupsFromDB[$dataset['owncloudname']] = $dataset;
219
+        }
220
+
221
+        return self::$groupsFromDB;
222
+    }
223 223
 }
Please login to merge, or discard this patch.