Completed
Push — master ( 08b58a...0efd05 )
by Daniel
30:08 queued 21s
created
apps/twofactor_backupcodes/lib/Provider/BackupCodesProvider.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -143,7 +143,7 @@
 block discarded – undo
143 143
 	 * @return boolean
144 144
 	 */
145 145
 	public function isActive(IUser $user): bool {
146
-		$appIds = array_filter($this->appManager->getEnabledAppsForUser($user), function ($appId) {
146
+		$appIds = array_filter($this->appManager->getEnabledAppsForUser($user), function($appId) {
147 147
 			return $appId !== $this->appName;
148 148
 		});
149 149
 		foreach ($appIds as $appId) {
Please login to merge, or discard this patch.
Indentation   +106 added lines, -106 removed lines patch added patch discarded remove patch
@@ -22,120 +22,120 @@
 block discarded – undo
22 22
 
23 23
 class BackupCodesProvider implements IDeactivatableByAdmin, IProvidesPersonalSettings {
24 24
 
25
-	/** @var AppManager */
26
-	private $appManager;
25
+    /** @var AppManager */
26
+    private $appManager;
27 27
 
28
-	/**
29
-	 * @param string $appName
30
-	 * @param BackupCodeStorage $storage
31
-	 * @param IL10N $l10n
32
-	 * @param AppManager $appManager
33
-	 */
34
-	public function __construct(
35
-		private string $appName,
36
-		private BackupCodeStorage $storage,
37
-		private IL10N $l10n,
38
-		AppManager $appManager,
39
-		private IInitialStateService $initialStateService,
40
-		private ITemplateManager $templateManager,
41
-	) {
42
-		$this->appManager = $appManager;
43
-	}
28
+    /**
29
+     * @param string $appName
30
+     * @param BackupCodeStorage $storage
31
+     * @param IL10N $l10n
32
+     * @param AppManager $appManager
33
+     */
34
+    public function __construct(
35
+        private string $appName,
36
+        private BackupCodeStorage $storage,
37
+        private IL10N $l10n,
38
+        AppManager $appManager,
39
+        private IInitialStateService $initialStateService,
40
+        private ITemplateManager $templateManager,
41
+    ) {
42
+        $this->appManager = $appManager;
43
+    }
44 44
 
45
-	/**
46
-	 * Get unique identifier of this 2FA provider
47
-	 *
48
-	 * @return string
49
-	 */
50
-	public function getId(): string {
51
-		return 'backup_codes';
52
-	}
45
+    /**
46
+     * Get unique identifier of this 2FA provider
47
+     *
48
+     * @return string
49
+     */
50
+    public function getId(): string {
51
+        return 'backup_codes';
52
+    }
53 53
 
54
-	/**
55
-	 * Get the display name for selecting the 2FA provider
56
-	 *
57
-	 * @return string
58
-	 */
59
-	public function getDisplayName(): string {
60
-		return $this->l10n->t('Backup code');
61
-	}
54
+    /**
55
+     * Get the display name for selecting the 2FA provider
56
+     *
57
+     * @return string
58
+     */
59
+    public function getDisplayName(): string {
60
+        return $this->l10n->t('Backup code');
61
+    }
62 62
 
63
-	/**
64
-	 * Get the description for selecting the 2FA provider
65
-	 *
66
-	 * @return string
67
-	 */
68
-	public function getDescription(): string {
69
-		return $this->l10n->t('Use backup code');
70
-	}
63
+    /**
64
+     * Get the description for selecting the 2FA provider
65
+     *
66
+     * @return string
67
+     */
68
+    public function getDescription(): string {
69
+        return $this->l10n->t('Use backup code');
70
+    }
71 71
 
72
-	/**
73
-	 * Get the template for rending the 2FA provider view
74
-	 *
75
-	 * @param IUser $user
76
-	 * @return ITemplate
77
-	 */
78
-	public function getTemplate(IUser $user): ITemplate {
79
-		return $this->templateManager->getTemplate('twofactor_backupcodes', 'challenge');
80
-	}
72
+    /**
73
+     * Get the template for rending the 2FA provider view
74
+     *
75
+     * @param IUser $user
76
+     * @return ITemplate
77
+     */
78
+    public function getTemplate(IUser $user): ITemplate {
79
+        return $this->templateManager->getTemplate('twofactor_backupcodes', 'challenge');
80
+    }
81 81
 
82
-	/**
83
-	 * Verify the given challenge
84
-	 *
85
-	 * @param IUser $user
86
-	 * @param string $challenge
87
-	 * @return bool
88
-	 */
89
-	public function verifyChallenge(IUser $user, string $challenge): bool {
90
-		return $this->storage->validateCode($user, $challenge);
91
-	}
82
+    /**
83
+     * Verify the given challenge
84
+     *
85
+     * @param IUser $user
86
+     * @param string $challenge
87
+     * @return bool
88
+     */
89
+    public function verifyChallenge(IUser $user, string $challenge): bool {
90
+        return $this->storage->validateCode($user, $challenge);
91
+    }
92 92
 
93
-	/**
94
-	 * Decides whether 2FA is enabled for the given user
95
-	 *
96
-	 * @param IUser $user
97
-	 * @return boolean
98
-	 */
99
-	public function isTwoFactorAuthEnabledForUser(IUser $user): bool {
100
-		return $this->storage->hasBackupCodes($user);
101
-	}
93
+    /**
94
+     * Decides whether 2FA is enabled for the given user
95
+     *
96
+     * @param IUser $user
97
+     * @return boolean
98
+     */
99
+    public function isTwoFactorAuthEnabledForUser(IUser $user): bool {
100
+        return $this->storage->hasBackupCodes($user);
101
+    }
102 102
 
103
-	/**
104
-	 * Determine whether backup codes should be active or not
105
-	 *
106
-	 * Backup codes only make sense if at least one 2FA provider is active,
107
-	 * hence this method checks all enabled apps on whether they provide 2FA
108
-	 * functionality or not. If there's at least one app, backup codes are
109
-	 * enabled on the personal settings page.
110
-	 *
111
-	 * @param IUser $user
112
-	 * @return boolean
113
-	 */
114
-	public function isActive(IUser $user): bool {
115
-		$appIds = array_filter($this->appManager->getEnabledAppsForUser($user), function ($appId) {
116
-			return $appId !== $this->appName;
117
-		});
118
-		foreach ($appIds as $appId) {
119
-			$info = $this->appManager->getAppInfo($appId);
120
-			if (isset($info['two-factor-providers']) && count($info['two-factor-providers']) > 0) {
121
-				return true;
122
-			}
123
-		}
124
-		return false;
125
-	}
103
+    /**
104
+     * Determine whether backup codes should be active or not
105
+     *
106
+     * Backup codes only make sense if at least one 2FA provider is active,
107
+     * hence this method checks all enabled apps on whether they provide 2FA
108
+     * functionality or not. If there's at least one app, backup codes are
109
+     * enabled on the personal settings page.
110
+     *
111
+     * @param IUser $user
112
+     * @return boolean
113
+     */
114
+    public function isActive(IUser $user): bool {
115
+        $appIds = array_filter($this->appManager->getEnabledAppsForUser($user), function ($appId) {
116
+            return $appId !== $this->appName;
117
+        });
118
+        foreach ($appIds as $appId) {
119
+            $info = $this->appManager->getAppInfo($appId);
120
+            if (isset($info['two-factor-providers']) && count($info['two-factor-providers']) > 0) {
121
+                return true;
122
+            }
123
+        }
124
+        return false;
125
+    }
126 126
 
127
-	/**
128
-	 * @param IUser $user
129
-	 *
130
-	 * @return IPersonalProviderSettings
131
-	 */
132
-	public function getPersonalSettings(IUser $user): IPersonalProviderSettings {
133
-		$state = $this->storage->getBackupCodesState($user);
134
-		$this->initialStateService->provideInitialState($this->appName, 'state', $state);
135
-		return new Personal();
136
-	}
127
+    /**
128
+     * @param IUser $user
129
+     *
130
+     * @return IPersonalProviderSettings
131
+     */
132
+    public function getPersonalSettings(IUser $user): IPersonalProviderSettings {
133
+        $state = $this->storage->getBackupCodesState($user);
134
+        $this->initialStateService->provideInitialState($this->appName, 'state', $state);
135
+        return new Personal();
136
+    }
137 137
 
138
-	public function disableFor(IUser $user) {
139
-		$this->storage->deleteCodes($user);
140
-	}
138
+    public function disableFor(IUser $user) {
139
+        $this->storage->deleteCodes($user);
140
+    }
141 141
 }
Please login to merge, or discard this patch.
apps/workflowengine/lib/Settings/ASettings.php 2 patches
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -146,8 +146,8 @@  discard block
 block discarded – undo
146 146
 	}
147 147
 
148 148
 	private function entitiesToArray(array $entities) {
149
-		return array_map(function (IEntity $entity) {
150
-			$events = array_map(function (IEntityEvent $entityEvent) {
149
+		return array_map(function(IEntity $entity) {
150
+			$events = array_map(function(IEntityEvent $entityEvent) {
151 151
 				return [
152 152
 					'eventName' => $entityEvent->getEventName(),
153 153
 					'displayName' => $entityEvent->getDisplayName()
@@ -164,11 +164,11 @@  discard block
 block discarded – undo
164 164
 	}
165 165
 
166 166
 	private function operatorsToArray(array $operators) {
167
-		$operators = array_filter($operators, function (IOperation $operator) {
167
+		$operators = array_filter($operators, function(IOperation $operator) {
168 168
 			return $operator->isAvailableForScope($this->getScope());
169 169
 		});
170 170
 
171
-		return array_map(function (IOperation $operator) {
171
+		return array_map(function(IOperation $operator) {
172 172
 			return [
173 173
 				'id' => get_class($operator),
174 174
 				'icon' => $operator->getIcon(),
@@ -182,11 +182,11 @@  discard block
 block discarded – undo
182 182
 	}
183 183
 
184 184
 	private function checksToArray(array $checks) {
185
-		$checks = array_filter($checks, function (ICheck $check) {
185
+		$checks = array_filter($checks, function(ICheck $check) {
186 186
 			return $check->isAvailableForScope($this->getScope());
187 187
 		});
188 188
 
189
-		return array_map(function (ICheck $check) {
189
+		return array_map(function(ICheck $check) {
190 190
 			return [
191 191
 				'id' => get_class($check),
192 192
 				'supportedEntities' => $check->supportedEntities(),
Please login to merge, or discard this patch.
Indentation   +126 added lines, -126 removed lines patch added patch discarded remove patch
@@ -26,130 +26,130 @@
 block discarded – undo
26 26
 use OCP\WorkflowEngine\ISpecificOperation;
27 27
 
28 28
 abstract class ASettings implements ISettings {
29
-	public function __construct(
30
-		private string $appName,
31
-		private IL10N $l10n,
32
-		private IEventDispatcher $eventDispatcher,
33
-		protected Manager $manager,
34
-		private IInitialState $initialStateService,
35
-		private IConfig $config,
36
-		private IURLGenerator $urlGenerator,
37
-	) {
38
-	}
39
-
40
-	abstract public function getScope(): int;
41
-
42
-	/**
43
-	 * @return TemplateResponse
44
-	 */
45
-	public function getForm(): TemplateResponse {
46
-		// @deprecated in 20.0.0: retire this one in favor of the typed event
47
-		$this->eventDispatcher->dispatch(
48
-			'OCP\WorkflowEngine::loadAdditionalSettingScripts',
49
-			new LoadSettingsScriptsEvent()
50
-		);
51
-		$this->eventDispatcher->dispatchTyped(new LoadSettingsScriptsEvent());
52
-
53
-		$entities = $this->manager->getEntitiesList();
54
-		$this->initialStateService->provideInitialState(
55
-			'entities',
56
-			$this->entitiesToArray($entities)
57
-		);
58
-
59
-		$operators = $this->manager->getOperatorList();
60
-		$this->initialStateService->provideInitialState(
61
-			'operators',
62
-			$this->operatorsToArray($operators)
63
-		);
64
-
65
-		$checks = $this->manager->getCheckList();
66
-		$this->initialStateService->provideInitialState(
67
-			'checks',
68
-			$this->checksToArray($checks)
69
-		);
70
-
71
-		$this->initialStateService->provideInitialState(
72
-			'scope',
73
-			$this->getScope()
74
-		);
75
-
76
-		$this->initialStateService->provideInitialState(
77
-			'appstoreenabled',
78
-			$this->config->getSystemValueBool('appstoreenabled', true)
79
-		);
80
-
81
-		$this->initialStateService->provideInitialState(
82
-			'doc-url',
83
-			$this->urlGenerator->linkToDocs('admin-workflowengine')
84
-		);
85
-
86
-		return new TemplateResponse(Application::APP_ID, 'settings', [], 'blank');
87
-	}
88
-
89
-	/**
90
-	 * @return string the section ID, e.g. 'sharing'
91
-	 */
92
-	public function getSection(): ?string {
93
-		return 'workflow';
94
-	}
95
-
96
-	/**
97
-	 * @return int whether the form should be rather on the top or bottom of
98
-	 *             the admin section. The forms are arranged in ascending order of the
99
-	 *             priority values. It is required to return a value between 0 and 100.
100
-	 *
101
-	 * E.g.: 70
102
-	 */
103
-	public function getPriority(): int {
104
-		return 0;
105
-	}
106
-
107
-	private function entitiesToArray(array $entities) {
108
-		return array_map(function (IEntity $entity) {
109
-			$events = array_map(function (IEntityEvent $entityEvent) {
110
-				return [
111
-					'eventName' => $entityEvent->getEventName(),
112
-					'displayName' => $entityEvent->getDisplayName()
113
-				];
114
-			}, $entity->getEvents());
115
-
116
-			return [
117
-				'id' => get_class($entity),
118
-				'icon' => $entity->getIcon(),
119
-				'name' => $entity->getName(),
120
-				'events' => $events,
121
-			];
122
-		}, $entities);
123
-	}
124
-
125
-	private function operatorsToArray(array $operators) {
126
-		$operators = array_filter($operators, function (IOperation $operator) {
127
-			return $operator->isAvailableForScope($this->getScope());
128
-		});
129
-
130
-		return array_map(function (IOperation $operator) {
131
-			return [
132
-				'id' => get_class($operator),
133
-				'icon' => $operator->getIcon(),
134
-				'name' => $operator->getDisplayName(),
135
-				'description' => $operator->getDescription(),
136
-				'fixedEntity' => $operator instanceof ISpecificOperation ? $operator->getEntityId() : '',
137
-				'isComplex' => $operator instanceof IComplexOperation,
138
-				'triggerHint' => $operator instanceof IComplexOperation ? $operator->getTriggerHint() : '',
139
-			];
140
-		}, $operators);
141
-	}
142
-
143
-	private function checksToArray(array $checks) {
144
-		$checks = array_filter($checks, function (ICheck $check) {
145
-			return $check->isAvailableForScope($this->getScope());
146
-		});
147
-
148
-		return array_map(function (ICheck $check) {
149
-			return [
150
-				'id' => get_class($check),
151
-				'supportedEntities' => $check->supportedEntities(),
152
-			];
153
-		}, $checks);
154
-	}
29
+    public function __construct(
30
+        private string $appName,
31
+        private IL10N $l10n,
32
+        private IEventDispatcher $eventDispatcher,
33
+        protected Manager $manager,
34
+        private IInitialState $initialStateService,
35
+        private IConfig $config,
36
+        private IURLGenerator $urlGenerator,
37
+    ) {
38
+    }
39
+
40
+    abstract public function getScope(): int;
41
+
42
+    /**
43
+     * @return TemplateResponse
44
+     */
45
+    public function getForm(): TemplateResponse {
46
+        // @deprecated in 20.0.0: retire this one in favor of the typed event
47
+        $this->eventDispatcher->dispatch(
48
+            'OCP\WorkflowEngine::loadAdditionalSettingScripts',
49
+            new LoadSettingsScriptsEvent()
50
+        );
51
+        $this->eventDispatcher->dispatchTyped(new LoadSettingsScriptsEvent());
52
+
53
+        $entities = $this->manager->getEntitiesList();
54
+        $this->initialStateService->provideInitialState(
55
+            'entities',
56
+            $this->entitiesToArray($entities)
57
+        );
58
+
59
+        $operators = $this->manager->getOperatorList();
60
+        $this->initialStateService->provideInitialState(
61
+            'operators',
62
+            $this->operatorsToArray($operators)
63
+        );
64
+
65
+        $checks = $this->manager->getCheckList();
66
+        $this->initialStateService->provideInitialState(
67
+            'checks',
68
+            $this->checksToArray($checks)
69
+        );
70
+
71
+        $this->initialStateService->provideInitialState(
72
+            'scope',
73
+            $this->getScope()
74
+        );
75
+
76
+        $this->initialStateService->provideInitialState(
77
+            'appstoreenabled',
78
+            $this->config->getSystemValueBool('appstoreenabled', true)
79
+        );
80
+
81
+        $this->initialStateService->provideInitialState(
82
+            'doc-url',
83
+            $this->urlGenerator->linkToDocs('admin-workflowengine')
84
+        );
85
+
86
+        return new TemplateResponse(Application::APP_ID, 'settings', [], 'blank');
87
+    }
88
+
89
+    /**
90
+     * @return string the section ID, e.g. 'sharing'
91
+     */
92
+    public function getSection(): ?string {
93
+        return 'workflow';
94
+    }
95
+
96
+    /**
97
+     * @return int whether the form should be rather on the top or bottom of
98
+     *             the admin section. The forms are arranged in ascending order of the
99
+     *             priority values. It is required to return a value between 0 and 100.
100
+     *
101
+     * E.g.: 70
102
+     */
103
+    public function getPriority(): int {
104
+        return 0;
105
+    }
106
+
107
+    private function entitiesToArray(array $entities) {
108
+        return array_map(function (IEntity $entity) {
109
+            $events = array_map(function (IEntityEvent $entityEvent) {
110
+                return [
111
+                    'eventName' => $entityEvent->getEventName(),
112
+                    'displayName' => $entityEvent->getDisplayName()
113
+                ];
114
+            }, $entity->getEvents());
115
+
116
+            return [
117
+                'id' => get_class($entity),
118
+                'icon' => $entity->getIcon(),
119
+                'name' => $entity->getName(),
120
+                'events' => $events,
121
+            ];
122
+        }, $entities);
123
+    }
124
+
125
+    private function operatorsToArray(array $operators) {
126
+        $operators = array_filter($operators, function (IOperation $operator) {
127
+            return $operator->isAvailableForScope($this->getScope());
128
+        });
129
+
130
+        return array_map(function (IOperation $operator) {
131
+            return [
132
+                'id' => get_class($operator),
133
+                'icon' => $operator->getIcon(),
134
+                'name' => $operator->getDisplayName(),
135
+                'description' => $operator->getDescription(),
136
+                'fixedEntity' => $operator instanceof ISpecificOperation ? $operator->getEntityId() : '',
137
+                'isComplex' => $operator instanceof IComplexOperation,
138
+                'triggerHint' => $operator instanceof IComplexOperation ? $operator->getTriggerHint() : '',
139
+            ];
140
+        }, $operators);
141
+    }
142
+
143
+    private function checksToArray(array $checks) {
144
+        $checks = array_filter($checks, function (ICheck $check) {
145
+            return $check->isAvailableForScope($this->getScope());
146
+        });
147
+
148
+        return array_map(function (ICheck $check) {
149
+            return [
150
+                'id' => get_class($check),
151
+                'supportedEntities' => $check->supportedEntities(),
152
+            ];
153
+        }, $checks);
154
+    }
155 155
 }
Please login to merge, or discard this patch.
lib/private/Authentication/TwoFactorAuth/ProviderSet.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -72,7 +72,7 @@
 block discarded – undo
72 72
 	 * @return IProvider[]
73 73
 	 */
74 74
 	public function getPrimaryProviders(): array {
75
-		return array_filter($this->providers, function (IProvider $provider) {
75
+		return array_filter($this->providers, function(IProvider $provider) {
76 76
 			return !($provider instanceof BackupCodesProvider);
77 77
 		});
78 78
 	}
Please login to merge, or discard this patch.
Indentation   +39 added lines, -39 removed lines patch added patch discarded remove patch
@@ -33,49 +33,49 @@
 block discarded – undo
33 33
  * Contains all two-factor provider information for the two-factor login challenge
34 34
  */
35 35
 class ProviderSet {
36
-	/** @var IProvider */
37
-	private $providers;
36
+    /** @var IProvider */
37
+    private $providers;
38 38
 
39
-	/** @var bool */
40
-	private $providerMissing;
39
+    /** @var bool */
40
+    private $providerMissing;
41 41
 
42
-	/**
43
-	 * @param IProvider[] $providers
44
-	 * @param bool $providerMissing
45
-	 */
46
-	public function __construct(array $providers, bool $providerMissing) {
47
-		$this->providers = [];
48
-		foreach ($providers as $provider) {
49
-			$this->providers[$provider->getId()] = $provider;
50
-		}
51
-		$this->providerMissing = $providerMissing;
52
-	}
42
+    /**
43
+     * @param IProvider[] $providers
44
+     * @param bool $providerMissing
45
+     */
46
+    public function __construct(array $providers, bool $providerMissing) {
47
+        $this->providers = [];
48
+        foreach ($providers as $provider) {
49
+            $this->providers[$provider->getId()] = $provider;
50
+        }
51
+        $this->providerMissing = $providerMissing;
52
+    }
53 53
 
54
-	/**
55
-	 * @param string $providerId
56
-	 * @return IProvider|null
57
-	 */
58
-	public function getProvider(string $providerId) {
59
-		return $this->providers[$providerId] ?? null;
60
-	}
54
+    /**
55
+     * @param string $providerId
56
+     * @return IProvider|null
57
+     */
58
+    public function getProvider(string $providerId) {
59
+        return $this->providers[$providerId] ?? null;
60
+    }
61 61
 
62
-	/**
63
-	 * @return IProvider[]
64
-	 */
65
-	public function getProviders(): array {
66
-		return $this->providers;
67
-	}
62
+    /**
63
+     * @return IProvider[]
64
+     */
65
+    public function getProviders(): array {
66
+        return $this->providers;
67
+    }
68 68
 
69
-	/**
70
-	 * @return IProvider[]
71
-	 */
72
-	public function getPrimaryProviders(): array {
73
-		return array_filter($this->providers, function (IProvider $provider) {
74
-			return !($provider instanceof BackupCodesProvider);
75
-		});
76
-	}
69
+    /**
70
+     * @return IProvider[]
71
+     */
72
+    public function getPrimaryProviders(): array {
73
+        return array_filter($this->providers, function (IProvider $provider) {
74
+            return !($provider instanceof BackupCodesProvider);
75
+        });
76
+    }
77 77
 
78
-	public function isProviderMissing(): bool {
79
-		return $this->providerMissing;
80
-	}
78
+    public function isProviderMissing(): bool {
79
+        return $this->providerMissing;
80
+    }
81 81
 }
Please login to merge, or discard this patch.
lib/private/Collaboration/Resources/Collection.php 2 patches
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -126,7 +126,7 @@  discard block
 block discarded – undo
126 126
 	 * @since 16.0.0
127 127
 	 */
128 128
 	public function addResource(IResource $resource): void {
129
-		array_map(function (IResource $r) use ($resource) {
129
+		array_map(function(IResource $r) use ($resource) {
130 130
 			if ($this->isSameResource($r, $resource)) {
131 131
 				throw new ResourceException('Already part of the collection');
132 132
 			}
@@ -158,7 +158,7 @@  discard block
 block discarded – undo
158 158
 	 * @since 16.0.0
159 159
 	 */
160 160
 	public function removeResource(IResource $resource): void {
161
-		$this->resources = array_filter($this->getResources(), function (IResource $r) use ($resource) {
161
+		$this->resources = array_filter($this->getResources(), function(IResource $r) use ($resource) {
162 162
 			return !$this->isSameResource($r, $resource);
163 163
 		});
164 164
 
Please login to merge, or discard this patch.
Indentation   +159 added lines, -159 removed lines patch added patch discarded remove patch
@@ -18,163 +18,163 @@
 block discarded – undo
18 18
 use OCP\IUser;
19 19
 
20 20
 class Collection implements ICollection {
21
-	/** @var IResource[] */
22
-	protected array $resources = [];
23
-
24
-	public function __construct(
25
-		/** @var Manager $manager */
26
-		protected IManager $manager,
27
-		protected IDBConnection $connection,
28
-		protected int $id,
29
-		protected string $name,
30
-		protected ?IUser $userForAccess = null,
31
-		protected ?bool $access = null,
32
-	) {
33
-	}
34
-
35
-	/**
36
-	 * @since 16.0.0
37
-	 */
38
-	public function getId(): int {
39
-		return $this->id;
40
-	}
41
-
42
-	/**
43
-	 * @since 16.0.0
44
-	 */
45
-	public function getName(): string {
46
-		return $this->name;
47
-	}
48
-
49
-	/**
50
-	 * @since 16.0.0
51
-	 */
52
-	public function setName(string $name): void {
53
-		$query = $this->connection->getQueryBuilder();
54
-		$query->update(Manager::TABLE_COLLECTIONS)
55
-			->set('name', $query->createNamedParameter($name))
56
-			->where($query->expr()->eq('id', $query->createNamedParameter($this->getId(), IQueryBuilder::PARAM_INT)));
57
-		$query->executeStatement();
58
-
59
-		$this->name = $name;
60
-	}
61
-
62
-	/**
63
-	 * @return IResource[]
64
-	 * @since 16.0.0
65
-	 */
66
-	public function getResources(): array {
67
-		if (empty($this->resources)) {
68
-			$this->resources = $this->manager->getResourcesByCollectionForUser($this, $this->userForAccess);
69
-		}
70
-
71
-		return $this->resources;
72
-	}
73
-
74
-	/**
75
-	 * Adds a resource to a collection
76
-	 *
77
-	 * @throws ResourceException when the resource is already part of the collection
78
-	 * @since 16.0.0
79
-	 */
80
-	public function addResource(IResource $resource): void {
81
-		array_map(function (IResource $r) use ($resource) {
82
-			if ($this->isSameResource($r, $resource)) {
83
-				throw new ResourceException('Already part of the collection');
84
-			}
85
-		}, $this->getResources());
86
-
87
-		$this->resources[] = $resource;
88
-
89
-		$query = $this->connection->getQueryBuilder();
90
-		$query->insert(Manager::TABLE_RESOURCES)
91
-			->values([
92
-				'collection_id' => $query->createNamedParameter($this->id, IQueryBuilder::PARAM_INT),
93
-				'resource_type' => $query->createNamedParameter($resource->getType()),
94
-				'resource_id' => $query->createNamedParameter($resource->getId()),
95
-			]);
96
-
97
-		try {
98
-			$query->execute();
99
-		} catch (ConstraintViolationException $e) {
100
-			throw new ResourceException('Already part of the collection');
101
-		}
102
-
103
-		$this->manager->invalidateAccessCacheForCollection($this);
104
-	}
105
-
106
-	/**
107
-	 * Removes a resource from a collection
108
-	 *
109
-	 * @since 16.0.0
110
-	 */
111
-	public function removeResource(IResource $resource): void {
112
-		$this->resources = array_filter($this->getResources(), function (IResource $r) use ($resource) {
113
-			return !$this->isSameResource($r, $resource);
114
-		});
115
-
116
-		$query = $this->connection->getQueryBuilder();
117
-		$query->delete(Manager::TABLE_RESOURCES)
118
-			->where($query->expr()->eq('collection_id', $query->createNamedParameter($this->id, IQueryBuilder::PARAM_INT)))
119
-			->andWhere($query->expr()->eq('resource_type', $query->createNamedParameter($resource->getType())))
120
-			->andWhere($query->expr()->eq('resource_id', $query->createNamedParameter($resource->getId())));
121
-		$query->executeStatement();
122
-
123
-		if (empty($this->resources)) {
124
-			$this->removeCollection();
125
-		} else {
126
-			$this->manager->invalidateAccessCacheForCollection($this);
127
-		}
128
-	}
129
-
130
-	/**
131
-	 * Can a user/guest access the collection
132
-	 *
133
-	 * @since 16.0.0
134
-	 */
135
-	public function canAccess(?IUser $user): bool {
136
-		if ($user instanceof IUser) {
137
-			return $this->canUserAccess($user);
138
-		}
139
-		return $this->canGuestAccess();
140
-	}
141
-
142
-	protected function canUserAccess(IUser $user): bool {
143
-		if (\is_bool($this->access) && $this->userForAccess instanceof IUser && $user->getUID() === $this->userForAccess->getUID()) {
144
-			return $this->access;
145
-		}
146
-
147
-		$access = $this->manager->canAccessCollection($this, $user);
148
-		if ($this->userForAccess instanceof IUser && $user->getUID() === $this->userForAccess->getUID()) {
149
-			$this->access = $access;
150
-		}
151
-		return $access;
152
-	}
153
-
154
-	protected function canGuestAccess(): bool {
155
-		if (\is_bool($this->access) && !$this->userForAccess instanceof IUser) {
156
-			return $this->access;
157
-		}
158
-
159
-		$access = $this->manager->canAccessCollection($this, null);
160
-		if (!$this->userForAccess instanceof IUser) {
161
-			$this->access = $access;
162
-		}
163
-		return $access;
164
-	}
165
-
166
-	protected function isSameResource(IResource $resource1, IResource $resource2): bool {
167
-		return $resource1->getType() === $resource2->getType() &&
168
-			$resource1->getId() === $resource2->getId();
169
-	}
170
-
171
-	protected function removeCollection(): void {
172
-		$query = $this->connection->getQueryBuilder();
173
-		$query->delete(Manager::TABLE_COLLECTIONS)
174
-			->where($query->expr()->eq('id', $query->createNamedParameter($this->id, IQueryBuilder::PARAM_INT)));
175
-		$query->executeStatement();
176
-
177
-		$this->manager->invalidateAccessCacheForCollection($this);
178
-		$this->id = 0;
179
-	}
21
+    /** @var IResource[] */
22
+    protected array $resources = [];
23
+
24
+    public function __construct(
25
+        /** @var Manager $manager */
26
+        protected IManager $manager,
27
+        protected IDBConnection $connection,
28
+        protected int $id,
29
+        protected string $name,
30
+        protected ?IUser $userForAccess = null,
31
+        protected ?bool $access = null,
32
+    ) {
33
+    }
34
+
35
+    /**
36
+     * @since 16.0.0
37
+     */
38
+    public function getId(): int {
39
+        return $this->id;
40
+    }
41
+
42
+    /**
43
+     * @since 16.0.0
44
+     */
45
+    public function getName(): string {
46
+        return $this->name;
47
+    }
48
+
49
+    /**
50
+     * @since 16.0.0
51
+     */
52
+    public function setName(string $name): void {
53
+        $query = $this->connection->getQueryBuilder();
54
+        $query->update(Manager::TABLE_COLLECTIONS)
55
+            ->set('name', $query->createNamedParameter($name))
56
+            ->where($query->expr()->eq('id', $query->createNamedParameter($this->getId(), IQueryBuilder::PARAM_INT)));
57
+        $query->executeStatement();
58
+
59
+        $this->name = $name;
60
+    }
61
+
62
+    /**
63
+     * @return IResource[]
64
+     * @since 16.0.0
65
+     */
66
+    public function getResources(): array {
67
+        if (empty($this->resources)) {
68
+            $this->resources = $this->manager->getResourcesByCollectionForUser($this, $this->userForAccess);
69
+        }
70
+
71
+        return $this->resources;
72
+    }
73
+
74
+    /**
75
+     * Adds a resource to a collection
76
+     *
77
+     * @throws ResourceException when the resource is already part of the collection
78
+     * @since 16.0.0
79
+     */
80
+    public function addResource(IResource $resource): void {
81
+        array_map(function (IResource $r) use ($resource) {
82
+            if ($this->isSameResource($r, $resource)) {
83
+                throw new ResourceException('Already part of the collection');
84
+            }
85
+        }, $this->getResources());
86
+
87
+        $this->resources[] = $resource;
88
+
89
+        $query = $this->connection->getQueryBuilder();
90
+        $query->insert(Manager::TABLE_RESOURCES)
91
+            ->values([
92
+                'collection_id' => $query->createNamedParameter($this->id, IQueryBuilder::PARAM_INT),
93
+                'resource_type' => $query->createNamedParameter($resource->getType()),
94
+                'resource_id' => $query->createNamedParameter($resource->getId()),
95
+            ]);
96
+
97
+        try {
98
+            $query->execute();
99
+        } catch (ConstraintViolationException $e) {
100
+            throw new ResourceException('Already part of the collection');
101
+        }
102
+
103
+        $this->manager->invalidateAccessCacheForCollection($this);
104
+    }
105
+
106
+    /**
107
+     * Removes a resource from a collection
108
+     *
109
+     * @since 16.0.0
110
+     */
111
+    public function removeResource(IResource $resource): void {
112
+        $this->resources = array_filter($this->getResources(), function (IResource $r) use ($resource) {
113
+            return !$this->isSameResource($r, $resource);
114
+        });
115
+
116
+        $query = $this->connection->getQueryBuilder();
117
+        $query->delete(Manager::TABLE_RESOURCES)
118
+            ->where($query->expr()->eq('collection_id', $query->createNamedParameter($this->id, IQueryBuilder::PARAM_INT)))
119
+            ->andWhere($query->expr()->eq('resource_type', $query->createNamedParameter($resource->getType())))
120
+            ->andWhere($query->expr()->eq('resource_id', $query->createNamedParameter($resource->getId())));
121
+        $query->executeStatement();
122
+
123
+        if (empty($this->resources)) {
124
+            $this->removeCollection();
125
+        } else {
126
+            $this->manager->invalidateAccessCacheForCollection($this);
127
+        }
128
+    }
129
+
130
+    /**
131
+     * Can a user/guest access the collection
132
+     *
133
+     * @since 16.0.0
134
+     */
135
+    public function canAccess(?IUser $user): bool {
136
+        if ($user instanceof IUser) {
137
+            return $this->canUserAccess($user);
138
+        }
139
+        return $this->canGuestAccess();
140
+    }
141
+
142
+    protected function canUserAccess(IUser $user): bool {
143
+        if (\is_bool($this->access) && $this->userForAccess instanceof IUser && $user->getUID() === $this->userForAccess->getUID()) {
144
+            return $this->access;
145
+        }
146
+
147
+        $access = $this->manager->canAccessCollection($this, $user);
148
+        if ($this->userForAccess instanceof IUser && $user->getUID() === $this->userForAccess->getUID()) {
149
+            $this->access = $access;
150
+        }
151
+        return $access;
152
+    }
153
+
154
+    protected function canGuestAccess(): bool {
155
+        if (\is_bool($this->access) && !$this->userForAccess instanceof IUser) {
156
+            return $this->access;
157
+        }
158
+
159
+        $access = $this->manager->canAccessCollection($this, null);
160
+        if (!$this->userForAccess instanceof IUser) {
161
+            $this->access = $access;
162
+        }
163
+        return $access;
164
+    }
165
+
166
+    protected function isSameResource(IResource $resource1, IResource $resource2): bool {
167
+        return $resource1->getType() === $resource2->getType() &&
168
+            $resource1->getId() === $resource2->getId();
169
+    }
170
+
171
+    protected function removeCollection(): void {
172
+        $query = $this->connection->getQueryBuilder();
173
+        $query->delete(Manager::TABLE_COLLECTIONS)
174
+            ->where($query->expr()->eq('id', $query->createNamedParameter($this->id, IQueryBuilder::PARAM_INT)));
175
+        $query->executeStatement();
176
+
177
+        $this->manager->invalidateAccessCacheForCollection($this);
178
+        $this->id = 0;
179
+    }
180 180
 }
Please login to merge, or discard this patch.
lib/public/Accounts/PropertyDoesNotExistException.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -38,7 +38,7 @@
 block discarded – undo
38 38
 	 * @since 15.0.0
39 39
 	 */
40 40
 	public function __construct($property) {
41
-		parent::__construct('Property ' . $property . ' does not exist.');
41
+		parent::__construct('Property '.$property.' does not exist.');
42 42
 	}
43 43
 
44 44
 }
Please login to merge, or discard this patch.
Indentation   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -30,12 +30,12 @@
 block discarded – undo
30 30
  *
31 31
  */
32 32
 class PropertyDoesNotExistException extends \Exception {
33
-	/**
34
-	 * Constructor
35
-	 * @param string $msg the error message
36
-	 * @since 15.0.0
37
-	 */
38
-	public function __construct($property) {
39
-		parent::__construct('Property ' . $property . ' does not exist.');
40
-	}
33
+    /**
34
+     * Constructor
35
+     * @param string $msg the error message
36
+     * @since 15.0.0
37
+     */
38
+    public function __construct($property) {
39
+        parent::__construct('Property ' . $property . ' does not exist.');
40
+    }
41 41
 }
Please login to merge, or discard this patch.
lib/private/Collaboration/Collaborators/RemotePlugin.php 2 patches
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -112,7 +112,7 @@  discard block
 block discarded – undo
112 112
 							$searchResult->markExactIdMatch($resultType);
113 113
 						}
114 114
 						$result['exact'][] = [
115
-							'label' => $contact['FN'] . " ($cloudId)",
115
+							'label' => $contact['FN']." ($cloudId)",
116 116
 							'uuid' => $contact['UID'],
117 117
 							'name' => $contact['FN'],
118 118
 							'type' => $cloudIdType,
@@ -124,7 +124,7 @@  discard block
 block discarded – undo
124 124
 						];
125 125
 					} else {
126 126
 						$result['wide'][] = [
127
-							'label' => $contact['FN'] . " ($cloudId)",
127
+							'label' => $contact['FN']." ($cloudId)",
128 128
 							'uuid' => $contact['UID'],
129 129
 							'name' => $contact['FN'],
130 130
 							'type' => $cloudIdType,
@@ -154,7 +154,7 @@  discard block
 block discarded – undo
154 154
 				$localUser = $this->userManager->get($remoteUser);
155 155
 				if ($localUser === null || $search !== $localUser->getCloudId()) {
156 156
 					$result['exact'][] = [
157
-						'label' => $remoteUser . " ($serverUrl)",
157
+						'label' => $remoteUser." ($serverUrl)",
158 158
 						'uuid' => $remoteUser,
159 159
 						'name' => $remoteUser,
160 160
 						'value' => [
Please login to merge, or discard this patch.
Indentation   +135 added lines, -135 removed lines patch added patch discarded remove patch
@@ -16,150 +16,150 @@
 block discarded – undo
16 16
 use OCP\Share\IShare;
17 17
 
18 18
 class RemotePlugin implements ISearchPlugin {
19
-	protected bool $shareeEnumeration;
19
+    protected bool $shareeEnumeration;
20 20
 
21
-	private string $userId;
21
+    private string $userId;
22 22
 
23
-	public function __construct(
24
-		private IManager $contactsManager,
25
-		private ICloudIdManager $cloudIdManager,
26
-		private IConfig $config,
27
-		private IUserManager $userManager,
28
-		IUserSession $userSession,
29
-	) {
30
-		$this->userId = $userSession->getUser()?->getUID() ?? '';
31
-		$this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
32
-	}
23
+    public function __construct(
24
+        private IManager $contactsManager,
25
+        private ICloudIdManager $cloudIdManager,
26
+        private IConfig $config,
27
+        private IUserManager $userManager,
28
+        IUserSession $userSession,
29
+    ) {
30
+        $this->userId = $userSession->getUser()?->getUID() ?? '';
31
+        $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes';
32
+    }
33 33
 
34
-	public function search($search, $limit, $offset, ISearchResult $searchResult): bool {
35
-		$result = ['wide' => [], 'exact' => []];
36
-		$resultType = new SearchResultType('remotes');
34
+    public function search($search, $limit, $offset, ISearchResult $searchResult): bool {
35
+        $result = ['wide' => [], 'exact' => []];
36
+        $resultType = new SearchResultType('remotes');
37 37
 
38
-		// Search in contacts
39
-		$addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN'], [
40
-			'limit' => $limit,
41
-			'offset' => $offset,
42
-			'enumeration' => false,
43
-			'fullmatch' => false,
44
-		]);
45
-		foreach ($addressBookContacts as $contact) {
46
-			if (isset($contact['isLocalSystemBook'])) {
47
-				continue;
48
-			}
49
-			if (isset($contact['CLOUD'])) {
50
-				$cloudIds = $contact['CLOUD'];
51
-				if (is_string($cloudIds)) {
52
-					$cloudIds = [$cloudIds];
53
-				}
54
-				$lowerSearch = strtolower($search);
55
-				foreach ($cloudIds as $cloudId) {
56
-					$cloudIdType = '';
57
-					if (\is_array($cloudId)) {
58
-						$cloudIdData = $cloudId;
59
-						$cloudId = $cloudIdData['value'];
60
-						$cloudIdType = $cloudIdData['type'];
61
-					}
62
-					try {
63
-						[$remoteUser, $serverUrl] = $this->splitUserRemote($cloudId);
64
-					} catch (\InvalidArgumentException $e) {
65
-						continue;
66
-					}
38
+        // Search in contacts
39
+        $addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN'], [
40
+            'limit' => $limit,
41
+            'offset' => $offset,
42
+            'enumeration' => false,
43
+            'fullmatch' => false,
44
+        ]);
45
+        foreach ($addressBookContacts as $contact) {
46
+            if (isset($contact['isLocalSystemBook'])) {
47
+                continue;
48
+            }
49
+            if (isset($contact['CLOUD'])) {
50
+                $cloudIds = $contact['CLOUD'];
51
+                if (is_string($cloudIds)) {
52
+                    $cloudIds = [$cloudIds];
53
+                }
54
+                $lowerSearch = strtolower($search);
55
+                foreach ($cloudIds as $cloudId) {
56
+                    $cloudIdType = '';
57
+                    if (\is_array($cloudId)) {
58
+                        $cloudIdData = $cloudId;
59
+                        $cloudId = $cloudIdData['value'];
60
+                        $cloudIdType = $cloudIdData['type'];
61
+                    }
62
+                    try {
63
+                        [$remoteUser, $serverUrl] = $this->splitUserRemote($cloudId);
64
+                    } catch (\InvalidArgumentException $e) {
65
+                        continue;
66
+                    }
67 67
 
68
-					$localUser = $this->userManager->get($remoteUser);
69
-					/**
70
-					 * Add local share if remote cloud id matches a local user ones
71
-					 */
72
-					if ($localUser !== null && $remoteUser !== $this->userId && $cloudId === $localUser->getCloudId()) {
73
-						$result['wide'][] = [
74
-							'label' => $contact['FN'],
75
-							'uuid' => $contact['UID'],
76
-							'value' => [
77
-								'shareType' => IShare::TYPE_USER,
78
-								'shareWith' => $remoteUser
79
-							],
80
-							'shareWithDisplayNameUnique' => $contact['EMAIL'] !== null && $contact['EMAIL'] !== '' ? $contact['EMAIL'] : $contact['UID'],
81
-						];
82
-					}
68
+                    $localUser = $this->userManager->get($remoteUser);
69
+                    /**
70
+                     * Add local share if remote cloud id matches a local user ones
71
+                     */
72
+                    if ($localUser !== null && $remoteUser !== $this->userId && $cloudId === $localUser->getCloudId()) {
73
+                        $result['wide'][] = [
74
+                            'label' => $contact['FN'],
75
+                            'uuid' => $contact['UID'],
76
+                            'value' => [
77
+                                'shareType' => IShare::TYPE_USER,
78
+                                'shareWith' => $remoteUser
79
+                            ],
80
+                            'shareWithDisplayNameUnique' => $contact['EMAIL'] !== null && $contact['EMAIL'] !== '' ? $contact['EMAIL'] : $contact['UID'],
81
+                        ];
82
+                    }
83 83
 
84
-					if (strtolower($contact['FN']) === $lowerSearch || strtolower($cloudId) === $lowerSearch) {
85
-						if (strtolower($cloudId) === $lowerSearch) {
86
-							$searchResult->markExactIdMatch($resultType);
87
-						}
88
-						$result['exact'][] = [
89
-							'label' => $contact['FN'] . " ($cloudId)",
90
-							'uuid' => $contact['UID'],
91
-							'name' => $contact['FN'],
92
-							'type' => $cloudIdType,
93
-							'value' => [
94
-								'shareType' => IShare::TYPE_REMOTE,
95
-								'shareWith' => $cloudId,
96
-								'server' => $serverUrl,
97
-							],
98
-						];
99
-					} else {
100
-						$result['wide'][] = [
101
-							'label' => $contact['FN'] . " ($cloudId)",
102
-							'uuid' => $contact['UID'],
103
-							'name' => $contact['FN'],
104
-							'type' => $cloudIdType,
105
-							'value' => [
106
-								'shareType' => IShare::TYPE_REMOTE,
107
-								'shareWith' => $cloudId,
108
-								'server' => $serverUrl,
109
-							],
110
-						];
111
-					}
112
-				}
113
-			}
114
-		}
84
+                    if (strtolower($contact['FN']) === $lowerSearch || strtolower($cloudId) === $lowerSearch) {
85
+                        if (strtolower($cloudId) === $lowerSearch) {
86
+                            $searchResult->markExactIdMatch($resultType);
87
+                        }
88
+                        $result['exact'][] = [
89
+                            'label' => $contact['FN'] . " ($cloudId)",
90
+                            'uuid' => $contact['UID'],
91
+                            'name' => $contact['FN'],
92
+                            'type' => $cloudIdType,
93
+                            'value' => [
94
+                                'shareType' => IShare::TYPE_REMOTE,
95
+                                'shareWith' => $cloudId,
96
+                                'server' => $serverUrl,
97
+                            ],
98
+                        ];
99
+                    } else {
100
+                        $result['wide'][] = [
101
+                            'label' => $contact['FN'] . " ($cloudId)",
102
+                            'uuid' => $contact['UID'],
103
+                            'name' => $contact['FN'],
104
+                            'type' => $cloudIdType,
105
+                            'value' => [
106
+                                'shareType' => IShare::TYPE_REMOTE,
107
+                                'shareWith' => $cloudId,
108
+                                'server' => $serverUrl,
109
+                            ],
110
+                        ];
111
+                    }
112
+                }
113
+            }
114
+        }
115 115
 
116
-		if (!$this->shareeEnumeration) {
117
-			$result['wide'] = [];
118
-		} else {
119
-			$result['wide'] = array_slice($result['wide'], $offset, $limit);
120
-		}
116
+        if (!$this->shareeEnumeration) {
117
+            $result['wide'] = [];
118
+        } else {
119
+            $result['wide'] = array_slice($result['wide'], $offset, $limit);
120
+        }
121 121
 
122
-		/**
123
-		 * Add generic share with remote item for valid cloud ids that are not users of the local instance
124
-		 */
125
-		if (!$searchResult->hasExactIdMatch($resultType) && $this->cloudIdManager->isValidCloudId($search) && $offset === 0) {
126
-			try {
127
-				[$remoteUser, $serverUrl] = $this->splitUserRemote($search);
128
-				$localUser = $this->userManager->get($remoteUser);
129
-				if ($localUser === null || $search !== $localUser->getCloudId()) {
130
-					$result['exact'][] = [
131
-						'label' => $remoteUser . " ($serverUrl)",
132
-						'uuid' => $remoteUser,
133
-						'name' => $remoteUser,
134
-						'value' => [
135
-							'shareType' => IShare::TYPE_REMOTE,
136
-							'shareWith' => $search,
137
-							'server' => $serverUrl,
138
-						],
139
-					];
140
-				}
141
-			} catch (\InvalidArgumentException $e) {
142
-			}
143
-		}
122
+        /**
123
+         * Add generic share with remote item for valid cloud ids that are not users of the local instance
124
+         */
125
+        if (!$searchResult->hasExactIdMatch($resultType) && $this->cloudIdManager->isValidCloudId($search) && $offset === 0) {
126
+            try {
127
+                [$remoteUser, $serverUrl] = $this->splitUserRemote($search);
128
+                $localUser = $this->userManager->get($remoteUser);
129
+                if ($localUser === null || $search !== $localUser->getCloudId()) {
130
+                    $result['exact'][] = [
131
+                        'label' => $remoteUser . " ($serverUrl)",
132
+                        'uuid' => $remoteUser,
133
+                        'name' => $remoteUser,
134
+                        'value' => [
135
+                            'shareType' => IShare::TYPE_REMOTE,
136
+                            'shareWith' => $search,
137
+                            'server' => $serverUrl,
138
+                        ],
139
+                    ];
140
+                }
141
+            } catch (\InvalidArgumentException $e) {
142
+            }
143
+        }
144 144
 
145
-		$searchResult->addResultSet($resultType, $result['wide'], $result['exact']);
145
+        $searchResult->addResultSet($resultType, $result['wide'], $result['exact']);
146 146
 
147
-		return true;
148
-	}
147
+        return true;
148
+    }
149 149
 
150
-	/**
151
-	 * split user and remote from federated cloud id
152
-	 *
153
-	 * @param string $address federated share address
154
-	 * @return array [user, remoteURL]
155
-	 * @throws \InvalidArgumentException
156
-	 */
157
-	public function splitUserRemote(string $address): array {
158
-		try {
159
-			$cloudId = $this->cloudIdManager->resolveCloudId($address);
160
-			return [$cloudId->getUser(), $this->cloudIdManager->removeProtocolFromUrl($cloudId->getRemote(), true)];
161
-		} catch (\InvalidArgumentException $e) {
162
-			throw new \InvalidArgumentException('Invalid Federated Cloud ID', 0, $e);
163
-		}
164
-	}
150
+    /**
151
+     * split user and remote from federated cloud id
152
+     *
153
+     * @param string $address federated share address
154
+     * @return array [user, remoteURL]
155
+     * @throws \InvalidArgumentException
156
+     */
157
+    public function splitUserRemote(string $address): array {
158
+        try {
159
+            $cloudId = $this->cloudIdManager->resolveCloudId($address);
160
+            return [$cloudId->getUser(), $this->cloudIdManager->removeProtocolFromUrl($cloudId->getRemote(), true)];
161
+        } catch (\InvalidArgumentException $e) {
162
+            throw new \InvalidArgumentException('Invalid Federated Cloud ID', 0, $e);
163
+        }
164
+    }
165 165
 }
Please login to merge, or discard this patch.
lib/private/Color.php 1 patch
Indentation   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -24,12 +24,12 @@
 block discarded – undo
24 24
 namespace OC;
25 25
 
26 26
 class Color {
27
-	public $r;
28
-	public $g;
29
-	public $b;
30
-	public function __construct($r, $g, $b) {
31
-		$this->r = $r;
32
-		$this->g = $g;
33
-		$this->b = $b;
34
-	}
27
+    public $r;
28
+    public $g;
29
+    public $b;
30
+    public function __construct($r, $g, $b) {
31
+        $this->r = $r;
32
+        $this->g = $g;
33
+        $this->b = $b;
34
+    }
35 35
 }
Please login to merge, or discard this patch.
apps/federatedfilesharing/lib/Controller/MountPublicLinkController.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -194,7 +194,7 @@
 block discarded – undo
194 194
 		$httpClient = $this->clientService->newClient();
195 195
 
196 196
 		try {
197
-			$response = $httpClient->post($remote . '/index.php/apps/federatedfilesharing/createFederatedShare',
197
+			$response = $httpClient->post($remote.'/index.php/apps/federatedfilesharing/createFederatedShare',
198 198
 				[
199 199
 					'body' =>
200 200
 						[
Please login to merge, or discard this patch.
Indentation   +144 added lines, -144 removed lines patch added patch discarded remove patch
@@ -37,148 +37,148 @@
 block discarded – undo
37 37
  * @package OCA\FederatedFileSharing\Controller
38 38
  */
39 39
 class MountPublicLinkController extends Controller {
40
-	/**
41
-	 * MountPublicLinkController constructor.
42
-	 */
43
-	public function __construct(
44
-		string $appName,
45
-		IRequest $request,
46
-		private FederatedShareProvider $federatedShareProvider,
47
-		private IManager $shareManager,
48
-		private AddressHandler $addressHandler,
49
-		private ISession $session,
50
-		private IL10N $l,
51
-		private IUserSession $userSession,
52
-		private IClientService $clientService,
53
-		private ICloudIdManager $cloudIdManager,
54
-		private LoggerInterface $logger,
55
-	) {
56
-		parent::__construct($appName, $request);
57
-	}
58
-
59
-	/**
60
-	 * send federated share to a user of a public link
61
-	 *
62
-	 * @param string $shareWith Username to share with
63
-	 * @param string $token Token of the share
64
-	 * @param string $password Password of the share
65
-	 * @return JSONResponse<Http::STATUS_OK, array{remoteUrl: string}, array{}>|JSONResponse<Http::STATUS_BAD_REQUEST, array{message: string}, array{}>
66
-	 *
67
-	 * 200: Remote URL returned
68
-	 * 400: Creating share is not possible
69
-	 */
70
-	#[NoCSRFRequired]
71
-	#[PublicPage]
72
-	#[BruteForceProtection(action: 'publicLink2FederatedShare')]
73
-	#[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)]
74
-	public function createFederatedShare($shareWith, $token, $password = '') {
75
-		if (!$this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) {
76
-			return new JSONResponse(
77
-				['message' => 'This server doesn\'t support outgoing federated shares'],
78
-				Http::STATUS_BAD_REQUEST
79
-			);
80
-		}
81
-
82
-		try {
83
-			[, $server] = $this->addressHandler->splitUserRemote($shareWith);
84
-			$share = $this->shareManager->getShareByToken($token);
85
-		} catch (HintException $e) {
86
-			$response = new JSONResponse(['message' => $e->getHint()], Http::STATUS_BAD_REQUEST);
87
-			$response->throttle();
88
-			return $response;
89
-		}
90
-
91
-		// make sure that user is authenticated in case of a password protected link
92
-		$storedPassword = $share->getPassword();
93
-		$authenticated = $this->session->get(PublicAuth::DAV_AUTHENTICATED) === $share->getId() ||
94
-			$this->shareManager->checkPassword($share, $password);
95
-		if (!empty($storedPassword) && !$authenticated) {
96
-			$response = new JSONResponse(
97
-				['message' => 'No permission to access the share'],
98
-				Http::STATUS_BAD_REQUEST
99
-			);
100
-			$response->throttle();
101
-			return $response;
102
-		}
103
-
104
-		if (($share->getPermissions() & Constants::PERMISSION_READ) === 0) {
105
-			$response = new JSONResponse(
106
-				['message' => 'Mounting file drop not supported'],
107
-				Http::STATUS_BAD_REQUEST
108
-			);
109
-			$response->throttle();
110
-			return $response;
111
-		}
112
-
113
-		$share->setSharedWith($shareWith);
114
-		$share->setShareType(IShare::TYPE_REMOTE);
115
-
116
-		try {
117
-			$this->federatedShareProvider->create($share);
118
-		} catch (\Exception $e) {
119
-			$this->logger->warning($e->getMessage(), [
120
-				'app' => 'federatedfilesharing',
121
-				'exception' => $e,
122
-			]);
123
-			return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
124
-		}
125
-
126
-		return new JSONResponse(['remoteUrl' => $server]);
127
-	}
128
-
129
-	/**
130
-	 * ask other server to get a federated share
131
-	 *
132
-	 * @param string $token
133
-	 * @param string $remote
134
-	 * @param string $password
135
-	 * @param string $owner (only for legacy reasons, can be removed with legacyMountPublicLink())
136
-	 * @param string $ownerDisplayName (only for legacy reasons, can be removed with legacyMountPublicLink())
137
-	 * @param string $name (only for legacy reasons, can be removed with legacyMountPublicLink())
138
-	 * @return JSONResponse
139
-	 */
140
-	#[NoAdminRequired]
141
-	public function askForFederatedShare($token, $remote, $password = '', $owner = '', $ownerDisplayName = '', $name = '') {
142
-		// check if server admin allows to mount public links from other servers
143
-		if ($this->federatedShareProvider->isIncomingServer2serverShareEnabled() === false) {
144
-			return new JSONResponse(['message' => $this->l->t('Server to server sharing is not enabled on this server')], Http::STATUS_BAD_REQUEST);
145
-		}
146
-
147
-		$cloudId = $this->cloudIdManager->getCloudId($this->userSession->getUser()->getUID(), $this->addressHandler->generateRemoteURL());
148
-
149
-		$httpClient = $this->clientService->newClient();
150
-
151
-		try {
152
-			$response = $httpClient->post($remote . '/index.php/apps/federatedfilesharing/createFederatedShare',
153
-				[
154
-					'body' =>
155
-						[
156
-							'token' => $token,
157
-							'shareWith' => rtrim($cloudId->getId(), '/'),
158
-							'password' => $password
159
-						],
160
-					'connect_timeout' => 10,
161
-				]
162
-			);
163
-		} catch (\Exception $e) {
164
-			if (empty($password)) {
165
-				$message = $this->l->t("Couldn't establish a federated share.");
166
-			} else {
167
-				$message = $this->l->t("Couldn't establish a federated share, maybe the password was wrong.");
168
-			}
169
-			return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
170
-		}
171
-
172
-		$body = $response->getBody();
173
-		$result = json_decode($body, true);
174
-
175
-		if (is_array($result) && isset($result['remoteUrl'])) {
176
-			return new JSONResponse(['message' => $this->l->t('Federated Share request sent, you will receive an invitation. Check your notifications.')]);
177
-		}
178
-
179
-		// if we doesn't get the expected response we assume that we try to add
180
-		// a federated share from a Nextcloud <= 9 server
181
-		$message = $this->l->t("Couldn't establish a federated share, it looks like the server to federate with is too old (Nextcloud <= 9).");
182
-		return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
183
-	}
40
+    /**
41
+     * MountPublicLinkController constructor.
42
+     */
43
+    public function __construct(
44
+        string $appName,
45
+        IRequest $request,
46
+        private FederatedShareProvider $federatedShareProvider,
47
+        private IManager $shareManager,
48
+        private AddressHandler $addressHandler,
49
+        private ISession $session,
50
+        private IL10N $l,
51
+        private IUserSession $userSession,
52
+        private IClientService $clientService,
53
+        private ICloudIdManager $cloudIdManager,
54
+        private LoggerInterface $logger,
55
+    ) {
56
+        parent::__construct($appName, $request);
57
+    }
58
+
59
+    /**
60
+     * send federated share to a user of a public link
61
+     *
62
+     * @param string $shareWith Username to share with
63
+     * @param string $token Token of the share
64
+     * @param string $password Password of the share
65
+     * @return JSONResponse<Http::STATUS_OK, array{remoteUrl: string}, array{}>|JSONResponse<Http::STATUS_BAD_REQUEST, array{message: string}, array{}>
66
+     *
67
+     * 200: Remote URL returned
68
+     * 400: Creating share is not possible
69
+     */
70
+    #[NoCSRFRequired]
71
+    #[PublicPage]
72
+    #[BruteForceProtection(action: 'publicLink2FederatedShare')]
73
+    #[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)]
74
+    public function createFederatedShare($shareWith, $token, $password = '') {
75
+        if (!$this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) {
76
+            return new JSONResponse(
77
+                ['message' => 'This server doesn\'t support outgoing federated shares'],
78
+                Http::STATUS_BAD_REQUEST
79
+            );
80
+        }
81
+
82
+        try {
83
+            [, $server] = $this->addressHandler->splitUserRemote($shareWith);
84
+            $share = $this->shareManager->getShareByToken($token);
85
+        } catch (HintException $e) {
86
+            $response = new JSONResponse(['message' => $e->getHint()], Http::STATUS_BAD_REQUEST);
87
+            $response->throttle();
88
+            return $response;
89
+        }
90
+
91
+        // make sure that user is authenticated in case of a password protected link
92
+        $storedPassword = $share->getPassword();
93
+        $authenticated = $this->session->get(PublicAuth::DAV_AUTHENTICATED) === $share->getId() ||
94
+            $this->shareManager->checkPassword($share, $password);
95
+        if (!empty($storedPassword) && !$authenticated) {
96
+            $response = new JSONResponse(
97
+                ['message' => 'No permission to access the share'],
98
+                Http::STATUS_BAD_REQUEST
99
+            );
100
+            $response->throttle();
101
+            return $response;
102
+        }
103
+
104
+        if (($share->getPermissions() & Constants::PERMISSION_READ) === 0) {
105
+            $response = new JSONResponse(
106
+                ['message' => 'Mounting file drop not supported'],
107
+                Http::STATUS_BAD_REQUEST
108
+            );
109
+            $response->throttle();
110
+            return $response;
111
+        }
112
+
113
+        $share->setSharedWith($shareWith);
114
+        $share->setShareType(IShare::TYPE_REMOTE);
115
+
116
+        try {
117
+            $this->federatedShareProvider->create($share);
118
+        } catch (\Exception $e) {
119
+            $this->logger->warning($e->getMessage(), [
120
+                'app' => 'federatedfilesharing',
121
+                'exception' => $e,
122
+            ]);
123
+            return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
124
+        }
125
+
126
+        return new JSONResponse(['remoteUrl' => $server]);
127
+    }
128
+
129
+    /**
130
+     * ask other server to get a federated share
131
+     *
132
+     * @param string $token
133
+     * @param string $remote
134
+     * @param string $password
135
+     * @param string $owner (only for legacy reasons, can be removed with legacyMountPublicLink())
136
+     * @param string $ownerDisplayName (only for legacy reasons, can be removed with legacyMountPublicLink())
137
+     * @param string $name (only for legacy reasons, can be removed with legacyMountPublicLink())
138
+     * @return JSONResponse
139
+     */
140
+    #[NoAdminRequired]
141
+    public function askForFederatedShare($token, $remote, $password = '', $owner = '', $ownerDisplayName = '', $name = '') {
142
+        // check if server admin allows to mount public links from other servers
143
+        if ($this->federatedShareProvider->isIncomingServer2serverShareEnabled() === false) {
144
+            return new JSONResponse(['message' => $this->l->t('Server to server sharing is not enabled on this server')], Http::STATUS_BAD_REQUEST);
145
+        }
146
+
147
+        $cloudId = $this->cloudIdManager->getCloudId($this->userSession->getUser()->getUID(), $this->addressHandler->generateRemoteURL());
148
+
149
+        $httpClient = $this->clientService->newClient();
150
+
151
+        try {
152
+            $response = $httpClient->post($remote . '/index.php/apps/federatedfilesharing/createFederatedShare',
153
+                [
154
+                    'body' =>
155
+                        [
156
+                            'token' => $token,
157
+                            'shareWith' => rtrim($cloudId->getId(), '/'),
158
+                            'password' => $password
159
+                        ],
160
+                    'connect_timeout' => 10,
161
+                ]
162
+            );
163
+        } catch (\Exception $e) {
164
+            if (empty($password)) {
165
+                $message = $this->l->t("Couldn't establish a federated share.");
166
+            } else {
167
+                $message = $this->l->t("Couldn't establish a federated share, maybe the password was wrong.");
168
+            }
169
+            return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
170
+        }
171
+
172
+        $body = $response->getBody();
173
+        $result = json_decode($body, true);
174
+
175
+        if (is_array($result) && isset($result['remoteUrl'])) {
176
+            return new JSONResponse(['message' => $this->l->t('Federated Share request sent, you will receive an invitation. Check your notifications.')]);
177
+        }
178
+
179
+        // if we doesn't get the expected response we assume that we try to add
180
+        // a federated share from a Nextcloud <= 9 server
181
+        $message = $this->l->t("Couldn't establish a federated share, it looks like the server to federate with is too old (Nextcloud <= 9).");
182
+        return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
183
+    }
184 184
 }
Please login to merge, or discard this patch.
lib/private/Lock/MemcacheLockingProvider.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -143,7 +143,7 @@
 block discarded – undo
143 143
 		} elseif ($existing === 'exclusive') {
144 144
 			return $existing;
145 145
 		} else {
146
-			return $existing . ' shared locks';
146
+			return $existing.' shared locks';
147 147
 		}
148 148
 	}
149 149
 }
Please login to merge, or discard this patch.
Indentation   +164 added lines, -164 removed lines patch added patch discarded remove patch
@@ -14,168 +14,168 @@
 block discarded – undo
14 14
 use OCP\Lock\LockedException;
15 15
 
16 16
 class MemcacheLockingProvider extends AbstractLockingProvider {
17
-	/** @var array<string, array{time: int, ttl: int}> */
18
-	private array $oldTTLs = [];
19
-
20
-	public function __construct(
21
-		private IMemcache $memcache,
22
-		private ITimeFactory $timeFactory,
23
-		int $ttl = 3600,
24
-	) {
25
-		parent::__construct($ttl);
26
-	}
27
-
28
-	private function setTTL(string $path, ?int $ttl = null, mixed $compare = null): void {
29
-		if (is_null($ttl)) {
30
-			$ttl = $this->ttl;
31
-		}
32
-		if ($this->memcache instanceof IMemcacheTTL) {
33
-			if ($compare !== null) {
34
-				$this->memcache->compareSetTTL($path, $compare, $ttl);
35
-			} else {
36
-				$this->memcache->setTTL($path, $ttl);
37
-			}
38
-		}
39
-	}
40
-
41
-	private function getTTL(string $path): int {
42
-		if ($this->memcache instanceof IMemcacheTTL) {
43
-			$ttl = $this->memcache->getTTL($path);
44
-			return $ttl === false ? -1 : $ttl;
45
-		} else {
46
-			return -1;
47
-		}
48
-	}
49
-
50
-	public function isLocked(string $path, int $type): bool {
51
-		$lockValue = $this->memcache->get($path);
52
-		if ($type === self::LOCK_SHARED) {
53
-			return is_int($lockValue) && $lockValue > 0;
54
-		} elseif ($type === self::LOCK_EXCLUSIVE) {
55
-			return $lockValue === 'exclusive';
56
-		} else {
57
-			return false;
58
-		}
59
-	}
60
-
61
-	public function acquireLock(string $path, int $type, ?string $readablePath = null): void {
62
-		if ($type === self::LOCK_SHARED) {
63
-			// save the old TTL to for `restoreTTL`
64
-			$this->oldTTLs[$path] = [
65
-				'ttl' => $this->getTTL($path),
66
-				'time' => $this->timeFactory->getTime()
67
-			];
68
-			if (!$this->memcache->inc($path)) {
69
-				throw new LockedException($path, null, $this->getExistingLockForException($path), $readablePath);
70
-			}
71
-		} else {
72
-			// when getting exclusive locks, we know there are no old TTLs to restore
73
-			$this->memcache->add($path, 0);
74
-			// ttl is updated automatically when the `set` succeeds
75
-			if (!$this->memcache->cas($path, 0, 'exclusive')) {
76
-				throw new LockedException($path, null, $this->getExistingLockForException($path), $readablePath);
77
-			}
78
-			unset($this->oldTTLs[$path]);
79
-		}
80
-		$this->setTTL($path);
81
-		$this->markAcquire($path, $type);
82
-	}
83
-
84
-	public function releaseLock(string $path, int $type): void {
85
-		if ($type === self::LOCK_SHARED) {
86
-			$ownSharedLockCount = $this->getOwnSharedLockCount($path);
87
-			$newValue = 0;
88
-			if ($ownSharedLockCount === 0) { // if we are not holding the lock, don't try to release it
89
-				return;
90
-			}
91
-			if ($ownSharedLockCount === 1) {
92
-				$removed = $this->memcache->cad($path, 1); // if we're the only one having a shared lock we can remove it in one go
93
-				if (!$removed) { //someone else also has a shared lock, decrease only
94
-					$newValue = $this->memcache->dec($path);
95
-				}
96
-			} else {
97
-				// if we own more than one lock ourselves just decrease
98
-				$newValue = $this->memcache->dec($path);
99
-			}
100
-
101
-			if ($newValue > 0) {
102
-				$this->restoreTTL($path);
103
-			} else {
104
-				unset($this->oldTTLs[$path]);
105
-			}
106
-
107
-			// if we somehow release more locks then exists, reset the lock
108
-			if ($newValue < 0) {
109
-				$this->memcache->cad($path, $newValue);
110
-			}
111
-		} elseif ($type === self::LOCK_EXCLUSIVE) {
112
-			$this->memcache->cad($path, 'exclusive');
113
-		}
114
-		$this->markRelease($path, $type);
115
-	}
116
-
117
-	public function changeLock(string $path, int $targetType): void {
118
-		if ($targetType === self::LOCK_SHARED) {
119
-			if (!$this->memcache->cas($path, 'exclusive', 1)) {
120
-				throw new LockedException($path, null, $this->getExistingLockForException($path));
121
-			}
122
-		} elseif ($targetType === self::LOCK_EXCLUSIVE) {
123
-			// we can only change a shared lock to an exclusive if there's only a single owner of the shared lock
124
-			if (!$this->memcache->cas($path, 1, 'exclusive')) {
125
-				$this->restoreTTL($path);
126
-				throw new LockedException($path, null, $this->getExistingLockForException($path));
127
-			}
128
-			unset($this->oldTTLs[$path]);
129
-		}
130
-		$this->setTTL($path);
131
-		$this->markChange($path, $targetType);
132
-	}
133
-
134
-	/**
135
-	 * With shared locks, each time the lock is acquired, the ttl for the path is reset.
136
-	 *
137
-	 * Due to this "ttl extension" when a shared lock isn't freed correctly for any reason
138
-	 * the lock won't expire until no shared locks are required for the path for 1h.
139
-	 * This can lead to a client repeatedly trying to upload a file, and failing forever
140
-	 * because the lock never gets the opportunity to expire.
141
-	 *
142
-	 * To help the lock expire in this case, we lower the TTL back to what it was before we
143
-	 * took the shared lock *only* if nobody else got a shared lock after we did.
144
-	 *
145
-	 * This doesn't handle all cases where multiple requests are acquiring shared locks
146
-	 * but it should handle some of the more common ones and not hurt things further
147
-	 */
148
-	private function restoreTTL(string $path): void {
149
-		if (isset($this->oldTTLs[$path])) {
150
-			$saved = $this->oldTTLs[$path];
151
-			$elapsed = $this->timeFactory->getTime() - $saved['time'];
152
-
153
-			// old value to compare to when setting ttl in case someone else changes the lock in the middle of this function
154
-			$value = $this->memcache->get($path);
155
-
156
-			$currentTtl = $this->getTTL($path);
157
-
158
-			// what the old ttl would be given the time elapsed since we acquired the lock
159
-			// note that if this gets negative the key will be expired directly when we set the ttl
160
-			$remainingOldTtl = $saved['ttl'] - $elapsed;
161
-			// what the currently ttl would be if nobody else acquired a lock since we did (+1 to cover rounding errors)
162
-			$expectedTtl = $this->ttl - $elapsed + 1;
163
-
164
-			// check if another request has acquired a lock (and didn't release it yet)
165
-			if ($currentTtl <= $expectedTtl) {
166
-				$this->setTTL($path, $remainingOldTtl, $value);
167
-			}
168
-		}
169
-	}
170
-
171
-	private function getExistingLockForException(string $path): string {
172
-		$existing = $this->memcache->get($path);
173
-		if (!$existing) {
174
-			return 'none';
175
-		} elseif ($existing === 'exclusive') {
176
-			return $existing;
177
-		} else {
178
-			return $existing . ' shared locks';
179
-		}
180
-	}
17
+    /** @var array<string, array{time: int, ttl: int}> */
18
+    private array $oldTTLs = [];
19
+
20
+    public function __construct(
21
+        private IMemcache $memcache,
22
+        private ITimeFactory $timeFactory,
23
+        int $ttl = 3600,
24
+    ) {
25
+        parent::__construct($ttl);
26
+    }
27
+
28
+    private function setTTL(string $path, ?int $ttl = null, mixed $compare = null): void {
29
+        if (is_null($ttl)) {
30
+            $ttl = $this->ttl;
31
+        }
32
+        if ($this->memcache instanceof IMemcacheTTL) {
33
+            if ($compare !== null) {
34
+                $this->memcache->compareSetTTL($path, $compare, $ttl);
35
+            } else {
36
+                $this->memcache->setTTL($path, $ttl);
37
+            }
38
+        }
39
+    }
40
+
41
+    private function getTTL(string $path): int {
42
+        if ($this->memcache instanceof IMemcacheTTL) {
43
+            $ttl = $this->memcache->getTTL($path);
44
+            return $ttl === false ? -1 : $ttl;
45
+        } else {
46
+            return -1;
47
+        }
48
+    }
49
+
50
+    public function isLocked(string $path, int $type): bool {
51
+        $lockValue = $this->memcache->get($path);
52
+        if ($type === self::LOCK_SHARED) {
53
+            return is_int($lockValue) && $lockValue > 0;
54
+        } elseif ($type === self::LOCK_EXCLUSIVE) {
55
+            return $lockValue === 'exclusive';
56
+        } else {
57
+            return false;
58
+        }
59
+    }
60
+
61
+    public function acquireLock(string $path, int $type, ?string $readablePath = null): void {
62
+        if ($type === self::LOCK_SHARED) {
63
+            // save the old TTL to for `restoreTTL`
64
+            $this->oldTTLs[$path] = [
65
+                'ttl' => $this->getTTL($path),
66
+                'time' => $this->timeFactory->getTime()
67
+            ];
68
+            if (!$this->memcache->inc($path)) {
69
+                throw new LockedException($path, null, $this->getExistingLockForException($path), $readablePath);
70
+            }
71
+        } else {
72
+            // when getting exclusive locks, we know there are no old TTLs to restore
73
+            $this->memcache->add($path, 0);
74
+            // ttl is updated automatically when the `set` succeeds
75
+            if (!$this->memcache->cas($path, 0, 'exclusive')) {
76
+                throw new LockedException($path, null, $this->getExistingLockForException($path), $readablePath);
77
+            }
78
+            unset($this->oldTTLs[$path]);
79
+        }
80
+        $this->setTTL($path);
81
+        $this->markAcquire($path, $type);
82
+    }
83
+
84
+    public function releaseLock(string $path, int $type): void {
85
+        if ($type === self::LOCK_SHARED) {
86
+            $ownSharedLockCount = $this->getOwnSharedLockCount($path);
87
+            $newValue = 0;
88
+            if ($ownSharedLockCount === 0) { // if we are not holding the lock, don't try to release it
89
+                return;
90
+            }
91
+            if ($ownSharedLockCount === 1) {
92
+                $removed = $this->memcache->cad($path, 1); // if we're the only one having a shared lock we can remove it in one go
93
+                if (!$removed) { //someone else also has a shared lock, decrease only
94
+                    $newValue = $this->memcache->dec($path);
95
+                }
96
+            } else {
97
+                // if we own more than one lock ourselves just decrease
98
+                $newValue = $this->memcache->dec($path);
99
+            }
100
+
101
+            if ($newValue > 0) {
102
+                $this->restoreTTL($path);
103
+            } else {
104
+                unset($this->oldTTLs[$path]);
105
+            }
106
+
107
+            // if we somehow release more locks then exists, reset the lock
108
+            if ($newValue < 0) {
109
+                $this->memcache->cad($path, $newValue);
110
+            }
111
+        } elseif ($type === self::LOCK_EXCLUSIVE) {
112
+            $this->memcache->cad($path, 'exclusive');
113
+        }
114
+        $this->markRelease($path, $type);
115
+    }
116
+
117
+    public function changeLock(string $path, int $targetType): void {
118
+        if ($targetType === self::LOCK_SHARED) {
119
+            if (!$this->memcache->cas($path, 'exclusive', 1)) {
120
+                throw new LockedException($path, null, $this->getExistingLockForException($path));
121
+            }
122
+        } elseif ($targetType === self::LOCK_EXCLUSIVE) {
123
+            // we can only change a shared lock to an exclusive if there's only a single owner of the shared lock
124
+            if (!$this->memcache->cas($path, 1, 'exclusive')) {
125
+                $this->restoreTTL($path);
126
+                throw new LockedException($path, null, $this->getExistingLockForException($path));
127
+            }
128
+            unset($this->oldTTLs[$path]);
129
+        }
130
+        $this->setTTL($path);
131
+        $this->markChange($path, $targetType);
132
+    }
133
+
134
+    /**
135
+     * With shared locks, each time the lock is acquired, the ttl for the path is reset.
136
+     *
137
+     * Due to this "ttl extension" when a shared lock isn't freed correctly for any reason
138
+     * the lock won't expire until no shared locks are required for the path for 1h.
139
+     * This can lead to a client repeatedly trying to upload a file, and failing forever
140
+     * because the lock never gets the opportunity to expire.
141
+     *
142
+     * To help the lock expire in this case, we lower the TTL back to what it was before we
143
+     * took the shared lock *only* if nobody else got a shared lock after we did.
144
+     *
145
+     * This doesn't handle all cases where multiple requests are acquiring shared locks
146
+     * but it should handle some of the more common ones and not hurt things further
147
+     */
148
+    private function restoreTTL(string $path): void {
149
+        if (isset($this->oldTTLs[$path])) {
150
+            $saved = $this->oldTTLs[$path];
151
+            $elapsed = $this->timeFactory->getTime() - $saved['time'];
152
+
153
+            // old value to compare to when setting ttl in case someone else changes the lock in the middle of this function
154
+            $value = $this->memcache->get($path);
155
+
156
+            $currentTtl = $this->getTTL($path);
157
+
158
+            // what the old ttl would be given the time elapsed since we acquired the lock
159
+            // note that if this gets negative the key will be expired directly when we set the ttl
160
+            $remainingOldTtl = $saved['ttl'] - $elapsed;
161
+            // what the currently ttl would be if nobody else acquired a lock since we did (+1 to cover rounding errors)
162
+            $expectedTtl = $this->ttl - $elapsed + 1;
163
+
164
+            // check if another request has acquired a lock (and didn't release it yet)
165
+            if ($currentTtl <= $expectedTtl) {
166
+                $this->setTTL($path, $remainingOldTtl, $value);
167
+            }
168
+        }
169
+    }
170
+
171
+    private function getExistingLockForException(string $path): string {
172
+        $existing = $this->memcache->get($path);
173
+        if (!$existing) {
174
+            return 'none';
175
+        } elseif ($existing === 'exclusive') {
176
+            return $existing;
177
+        } else {
178
+            return $existing . ' shared locks';
179
+        }
180
+    }
181 181
 }
Please login to merge, or discard this patch.