Passed
Push — master ( ac4ff6...d0cf20 )
by John
23:45 queued 10:21
created
core/Application.php 1 patch
Indentation   +214 added lines, -214 removed lines patch added patch discarded remove patch
@@ -61,218 +61,218 @@
 block discarded – undo
61 61
  * @package OC\Core
62 62
  */
63 63
 class Application extends App {
64
-	public function __construct() {
65
-		parent::__construct('core');
66
-
67
-		$container = $this->getContainer();
68
-
69
-		$container->registerService('defaultMailAddress', function () {
70
-			return Util::getDefaultEmailAddress('lostpassword-noreply');
71
-		});
72
-
73
-		$server = $container->getServer();
74
-		/** @var IEventDispatcher $eventDispatcher */
75
-		$eventDispatcher = $server->query(IEventDispatcher::class);
76
-
77
-		$notificationManager = $server->getNotificationManager();
78
-		$notificationManager->registerNotifierService(CoreNotifier::class);
79
-		$notificationManager->registerNotifierService(AuthenticationNotifier::class);
80
-
81
-		$oldEventDispatcher = $server->getEventDispatcher();
82
-
83
-		$oldEventDispatcher->addListener(IDBConnection::CHECK_MISSING_INDEXES_EVENT,
84
-			function (GenericEvent $event) use ($container) {
85
-				/** @var MissingIndexInformation $subject */
86
-				$subject = $event->getSubject();
87
-
88
-				$schema = new SchemaWrapper($container->query(Connection::class));
89
-
90
-				if ($schema->hasTable('share')) {
91
-					$table = $schema->getTable('share');
92
-
93
-					if (!$table->hasIndex('share_with_index')) {
94
-						$subject->addHintForMissingSubject($table->getName(), 'share_with_index');
95
-					}
96
-					if (!$table->hasIndex('parent_index')) {
97
-						$subject->addHintForMissingSubject($table->getName(), 'parent_index');
98
-					}
99
-					if (!$table->hasIndex('owner_index')) {
100
-						$subject->addHintForMissingSubject($table->getName(), 'owner_index');
101
-					}
102
-					if (!$table->hasIndex('initiator_index')) {
103
-						$subject->addHintForMissingSubject($table->getName(), 'initiator_index');
104
-					}
105
-				}
106
-
107
-				if ($schema->hasTable('filecache')) {
108
-					$table = $schema->getTable('filecache');
109
-
110
-					if (!$table->hasIndex('fs_mtime')) {
111
-						$subject->addHintForMissingSubject($table->getName(), 'fs_mtime');
112
-					}
113
-
114
-					if (!$table->hasIndex('fs_size')) {
115
-						$subject->addHintForMissingSubject($table->getName(), 'fs_size');
116
-					}
117
-				}
118
-
119
-				if ($schema->hasTable('twofactor_providers')) {
120
-					$table = $schema->getTable('twofactor_providers');
121
-
122
-					if (!$table->hasIndex('twofactor_providers_uid')) {
123
-						$subject->addHintForMissingSubject($table->getName(), 'twofactor_providers_uid');
124
-					}
125
-				}
126
-
127
-				if ($schema->hasTable('login_flow_v2')) {
128
-					$table = $schema->getTable('login_flow_v2');
129
-
130
-					if (!$table->hasIndex('poll_token')) {
131
-						$subject->addHintForMissingSubject($table->getName(), 'poll_token');
132
-					}
133
-					if (!$table->hasIndex('login_token')) {
134
-						$subject->addHintForMissingSubject($table->getName(), 'login_token');
135
-					}
136
-					if (!$table->hasIndex('timestamp')) {
137
-						$subject->addHintForMissingSubject($table->getName(), 'timestamp');
138
-					}
139
-				}
140
-
141
-				if ($schema->hasTable('whats_new')) {
142
-					$table = $schema->getTable('whats_new');
143
-
144
-					if (!$table->hasIndex('version')) {
145
-						$subject->addHintForMissingSubject($table->getName(), 'version');
146
-					}
147
-				}
148
-
149
-				if ($schema->hasTable('cards')) {
150
-					$table = $schema->getTable('cards');
151
-
152
-					if (!$table->hasIndex('cards_abid')) {
153
-						$subject->addHintForMissingSubject($table->getName(), 'cards_abid');
154
-					}
155
-
156
-					if (!$table->hasIndex('cards_abiduri')) {
157
-						$subject->addHintForMissingSubject($table->getName(), 'cards_abiduri');
158
-					}
159
-				}
160
-
161
-				if ($schema->hasTable('cards_properties')) {
162
-					$table = $schema->getTable('cards_properties');
163
-
164
-					if (!$table->hasIndex('cards_prop_abid')) {
165
-						$subject->addHintForMissingSubject($table->getName(), 'cards_prop_abid');
166
-					}
167
-				}
168
-
169
-				if ($schema->hasTable('calendarobjects_props')) {
170
-					$table = $schema->getTable('calendarobjects_props');
171
-
172
-					if (!$table->hasIndex('calendarobject_calid_index')) {
173
-						$subject->addHintForMissingSubject($table->getName(), 'calendarobject_calid_index');
174
-					}
175
-				}
176
-
177
-				if ($schema->hasTable('schedulingobjects')) {
178
-					$table = $schema->getTable('schedulingobjects');
179
-					if (!$table->hasIndex('schedulobj_principuri_index')) {
180
-						$subject->addHintForMissingSubject($table->getName(), 'schedulobj_principuri_index');
181
-					}
182
-				}
183
-
184
-				if ($schema->hasTable('properties')) {
185
-					$table = $schema->getTable('properties');
186
-					if (!$table->hasIndex('properties_path_index')) {
187
-						$subject->addHintForMissingSubject($table->getName(), 'properties_path_index');
188
-					}
189
-				}
190
-			}
191
-		);
192
-
193
-		$oldEventDispatcher->addListener(IDBConnection::CHECK_MISSING_PRIMARY_KEYS_EVENT,
194
-			function (GenericEvent $event) use ($container) {
195
-				/** @var MissingPrimaryKeyInformation $subject */
196
-				$subject = $event->getSubject();
197
-
198
-				$schema = new SchemaWrapper($container->query(Connection::class));
199
-
200
-				if ($schema->hasTable('federated_reshares')) {
201
-					$table = $schema->getTable('federated_reshares');
202
-
203
-					if (!$table->hasPrimaryKey()) {
204
-						$subject->addHintForMissingSubject($table->getName());
205
-					}
206
-				}
207
-
208
-				if ($schema->hasTable('systemtag_object_mapping')) {
209
-					$table = $schema->getTable('systemtag_object_mapping');
210
-
211
-					if (!$table->hasPrimaryKey()) {
212
-						$subject->addHintForMissingSubject($table->getName());
213
-					}
214
-				}
215
-
216
-				if ($schema->hasTable('comments_read_markers')) {
217
-					$table = $schema->getTable('comments_read_markers');
218
-
219
-					if (!$table->hasPrimaryKey()) {
220
-						$subject->addHintForMissingSubject($table->getName());
221
-					}
222
-				}
223
-
224
-				if ($schema->hasTable('collres_resources')) {
225
-					$table = $schema->getTable('collres_resources');
226
-
227
-					if (!$table->hasPrimaryKey()) {
228
-						$subject->addHintForMissingSubject($table->getName());
229
-					}
230
-				}
231
-
232
-				if ($schema->hasTable('collres_accesscache')) {
233
-					$table = $schema->getTable('collres_accesscache');
234
-
235
-					if (!$table->hasPrimaryKey()) {
236
-						$subject->addHintForMissingSubject($table->getName());
237
-					}
238
-				}
239
-
240
-				if ($schema->hasTable('filecache_extended')) {
241
-					$table = $schema->getTable('filecache_extended');
242
-
243
-					if (!$table->hasPrimaryKey()) {
244
-						$subject->addHintForMissingSubject($table->getName());
245
-					}
246
-				}
247
-			}
248
-		);
249
-
250
-		$oldEventDispatcher->addListener(IDBConnection::CHECK_MISSING_COLUMNS_EVENT,
251
-			function (GenericEvent $event) use ($container) {
252
-				/** @var MissingColumnInformation $subject */
253
-				$subject = $event->getSubject();
254
-
255
-				$schema = new SchemaWrapper($container->query(Connection::class));
256
-
257
-				if ($schema->hasTable('comments')) {
258
-					$table = $schema->getTable('comments');
259
-
260
-					if (!$table->hasColumn('reference_id')) {
261
-						$subject->addHintForMissingColumn($table->getName(), 'reference_id');
262
-					}
263
-				}
264
-			}
265
-		);
266
-
267
-		$eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeActivityListener::class);
268
-		$eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeNotificationsListener::class);
269
-		$eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeEmailListener::class);
270
-		$eventDispatcher->addServiceListener(RemoteWipeFinished::class, RemoteWipeActivityListener::class);
271
-		$eventDispatcher->addServiceListener(RemoteWipeFinished::class, RemoteWipeNotificationsListener::class);
272
-		$eventDispatcher->addServiceListener(RemoteWipeFinished::class, RemoteWipeEmailListener::class);
273
-		$eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedStoreCleanupListener::class);
274
-		$eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedTokenCleanupListener::class);
275
-		$eventDispatcher->addServiceListener(BeforeUserDeletedEvent::class, UserDeletedFilesCleanupListener::class);
276
-		$eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedFilesCleanupListener::class);
277
-	}
64
+    public function __construct() {
65
+        parent::__construct('core');
66
+
67
+        $container = $this->getContainer();
68
+
69
+        $container->registerService('defaultMailAddress', function () {
70
+            return Util::getDefaultEmailAddress('lostpassword-noreply');
71
+        });
72
+
73
+        $server = $container->getServer();
74
+        /** @var IEventDispatcher $eventDispatcher */
75
+        $eventDispatcher = $server->query(IEventDispatcher::class);
76
+
77
+        $notificationManager = $server->getNotificationManager();
78
+        $notificationManager->registerNotifierService(CoreNotifier::class);
79
+        $notificationManager->registerNotifierService(AuthenticationNotifier::class);
80
+
81
+        $oldEventDispatcher = $server->getEventDispatcher();
82
+
83
+        $oldEventDispatcher->addListener(IDBConnection::CHECK_MISSING_INDEXES_EVENT,
84
+            function (GenericEvent $event) use ($container) {
85
+                /** @var MissingIndexInformation $subject */
86
+                $subject = $event->getSubject();
87
+
88
+                $schema = new SchemaWrapper($container->query(Connection::class));
89
+
90
+                if ($schema->hasTable('share')) {
91
+                    $table = $schema->getTable('share');
92
+
93
+                    if (!$table->hasIndex('share_with_index')) {
94
+                        $subject->addHintForMissingSubject($table->getName(), 'share_with_index');
95
+                    }
96
+                    if (!$table->hasIndex('parent_index')) {
97
+                        $subject->addHintForMissingSubject($table->getName(), 'parent_index');
98
+                    }
99
+                    if (!$table->hasIndex('owner_index')) {
100
+                        $subject->addHintForMissingSubject($table->getName(), 'owner_index');
101
+                    }
102
+                    if (!$table->hasIndex('initiator_index')) {
103
+                        $subject->addHintForMissingSubject($table->getName(), 'initiator_index');
104
+                    }
105
+                }
106
+
107
+                if ($schema->hasTable('filecache')) {
108
+                    $table = $schema->getTable('filecache');
109
+
110
+                    if (!$table->hasIndex('fs_mtime')) {
111
+                        $subject->addHintForMissingSubject($table->getName(), 'fs_mtime');
112
+                    }
113
+
114
+                    if (!$table->hasIndex('fs_size')) {
115
+                        $subject->addHintForMissingSubject($table->getName(), 'fs_size');
116
+                    }
117
+                }
118
+
119
+                if ($schema->hasTable('twofactor_providers')) {
120
+                    $table = $schema->getTable('twofactor_providers');
121
+
122
+                    if (!$table->hasIndex('twofactor_providers_uid')) {
123
+                        $subject->addHintForMissingSubject($table->getName(), 'twofactor_providers_uid');
124
+                    }
125
+                }
126
+
127
+                if ($schema->hasTable('login_flow_v2')) {
128
+                    $table = $schema->getTable('login_flow_v2');
129
+
130
+                    if (!$table->hasIndex('poll_token')) {
131
+                        $subject->addHintForMissingSubject($table->getName(), 'poll_token');
132
+                    }
133
+                    if (!$table->hasIndex('login_token')) {
134
+                        $subject->addHintForMissingSubject($table->getName(), 'login_token');
135
+                    }
136
+                    if (!$table->hasIndex('timestamp')) {
137
+                        $subject->addHintForMissingSubject($table->getName(), 'timestamp');
138
+                    }
139
+                }
140
+
141
+                if ($schema->hasTable('whats_new')) {
142
+                    $table = $schema->getTable('whats_new');
143
+
144
+                    if (!$table->hasIndex('version')) {
145
+                        $subject->addHintForMissingSubject($table->getName(), 'version');
146
+                    }
147
+                }
148
+
149
+                if ($schema->hasTable('cards')) {
150
+                    $table = $schema->getTable('cards');
151
+
152
+                    if (!$table->hasIndex('cards_abid')) {
153
+                        $subject->addHintForMissingSubject($table->getName(), 'cards_abid');
154
+                    }
155
+
156
+                    if (!$table->hasIndex('cards_abiduri')) {
157
+                        $subject->addHintForMissingSubject($table->getName(), 'cards_abiduri');
158
+                    }
159
+                }
160
+
161
+                if ($schema->hasTable('cards_properties')) {
162
+                    $table = $schema->getTable('cards_properties');
163
+
164
+                    if (!$table->hasIndex('cards_prop_abid')) {
165
+                        $subject->addHintForMissingSubject($table->getName(), 'cards_prop_abid');
166
+                    }
167
+                }
168
+
169
+                if ($schema->hasTable('calendarobjects_props')) {
170
+                    $table = $schema->getTable('calendarobjects_props');
171
+
172
+                    if (!$table->hasIndex('calendarobject_calid_index')) {
173
+                        $subject->addHintForMissingSubject($table->getName(), 'calendarobject_calid_index');
174
+                    }
175
+                }
176
+
177
+                if ($schema->hasTable('schedulingobjects')) {
178
+                    $table = $schema->getTable('schedulingobjects');
179
+                    if (!$table->hasIndex('schedulobj_principuri_index')) {
180
+                        $subject->addHintForMissingSubject($table->getName(), 'schedulobj_principuri_index');
181
+                    }
182
+                }
183
+
184
+                if ($schema->hasTable('properties')) {
185
+                    $table = $schema->getTable('properties');
186
+                    if (!$table->hasIndex('properties_path_index')) {
187
+                        $subject->addHintForMissingSubject($table->getName(), 'properties_path_index');
188
+                    }
189
+                }
190
+            }
191
+        );
192
+
193
+        $oldEventDispatcher->addListener(IDBConnection::CHECK_MISSING_PRIMARY_KEYS_EVENT,
194
+            function (GenericEvent $event) use ($container) {
195
+                /** @var MissingPrimaryKeyInformation $subject */
196
+                $subject = $event->getSubject();
197
+
198
+                $schema = new SchemaWrapper($container->query(Connection::class));
199
+
200
+                if ($schema->hasTable('federated_reshares')) {
201
+                    $table = $schema->getTable('federated_reshares');
202
+
203
+                    if (!$table->hasPrimaryKey()) {
204
+                        $subject->addHintForMissingSubject($table->getName());
205
+                    }
206
+                }
207
+
208
+                if ($schema->hasTable('systemtag_object_mapping')) {
209
+                    $table = $schema->getTable('systemtag_object_mapping');
210
+
211
+                    if (!$table->hasPrimaryKey()) {
212
+                        $subject->addHintForMissingSubject($table->getName());
213
+                    }
214
+                }
215
+
216
+                if ($schema->hasTable('comments_read_markers')) {
217
+                    $table = $schema->getTable('comments_read_markers');
218
+
219
+                    if (!$table->hasPrimaryKey()) {
220
+                        $subject->addHintForMissingSubject($table->getName());
221
+                    }
222
+                }
223
+
224
+                if ($schema->hasTable('collres_resources')) {
225
+                    $table = $schema->getTable('collres_resources');
226
+
227
+                    if (!$table->hasPrimaryKey()) {
228
+                        $subject->addHintForMissingSubject($table->getName());
229
+                    }
230
+                }
231
+
232
+                if ($schema->hasTable('collres_accesscache')) {
233
+                    $table = $schema->getTable('collres_accesscache');
234
+
235
+                    if (!$table->hasPrimaryKey()) {
236
+                        $subject->addHintForMissingSubject($table->getName());
237
+                    }
238
+                }
239
+
240
+                if ($schema->hasTable('filecache_extended')) {
241
+                    $table = $schema->getTable('filecache_extended');
242
+
243
+                    if (!$table->hasPrimaryKey()) {
244
+                        $subject->addHintForMissingSubject($table->getName());
245
+                    }
246
+                }
247
+            }
248
+        );
249
+
250
+        $oldEventDispatcher->addListener(IDBConnection::CHECK_MISSING_COLUMNS_EVENT,
251
+            function (GenericEvent $event) use ($container) {
252
+                /** @var MissingColumnInformation $subject */
253
+                $subject = $event->getSubject();
254
+
255
+                $schema = new SchemaWrapper($container->query(Connection::class));
256
+
257
+                if ($schema->hasTable('comments')) {
258
+                    $table = $schema->getTable('comments');
259
+
260
+                    if (!$table->hasColumn('reference_id')) {
261
+                        $subject->addHintForMissingColumn($table->getName(), 'reference_id');
262
+                    }
263
+                }
264
+            }
265
+        );
266
+
267
+        $eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeActivityListener::class);
268
+        $eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeNotificationsListener::class);
269
+        $eventDispatcher->addServiceListener(RemoteWipeStarted::class, RemoteWipeEmailListener::class);
270
+        $eventDispatcher->addServiceListener(RemoteWipeFinished::class, RemoteWipeActivityListener::class);
271
+        $eventDispatcher->addServiceListener(RemoteWipeFinished::class, RemoteWipeNotificationsListener::class);
272
+        $eventDispatcher->addServiceListener(RemoteWipeFinished::class, RemoteWipeEmailListener::class);
273
+        $eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedStoreCleanupListener::class);
274
+        $eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedTokenCleanupListener::class);
275
+        $eventDispatcher->addServiceListener(BeforeUserDeletedEvent::class, UserDeletedFilesCleanupListener::class);
276
+        $eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedFilesCleanupListener::class);
277
+    }
278 278
 }
Please login to merge, or discard this patch.
lib/private/Authentication/Listeners/UserDeletedFilesCleanupListener.php 1 patch
Indentation   +34 added lines, -34 removed lines patch added patch discarded remove patch
@@ -32,42 +32,42 @@
 block discarded – undo
32 32
 use OCP\User\Events\UserDeletedEvent;
33 33
 
34 34
 class UserDeletedFilesCleanupListener implements IEventListener {
35
-	/** @var array<string,IStorage> */
36
-	private $homeStorageCache = [];
35
+    /** @var array<string,IStorage> */
36
+    private $homeStorageCache = [];
37 37
 
38
-	/** @var IMountProviderCollection */
39
-	private $mountProviderCollection;
38
+    /** @var IMountProviderCollection */
39
+    private $mountProviderCollection;
40 40
 
41
-	public function __construct(IMountProviderCollection $mountProviderCollection) {
42
-		$this->mountProviderCollection = $mountProviderCollection;
43
-	}
41
+    public function __construct(IMountProviderCollection $mountProviderCollection) {
42
+        $this->mountProviderCollection = $mountProviderCollection;
43
+    }
44 44
 
45
-	public function handle(Event $event): void {
46
-		// since we can't reliably get the user home storage after the user is deleted
47
-		// but the user deletion might get canceled during the before event
48
-		// we only cache the user home storage during the before event and then do the
49
-		// action deletion during the after event
45
+    public function handle(Event $event): void {
46
+        // since we can't reliably get the user home storage after the user is deleted
47
+        // but the user deletion might get canceled during the before event
48
+        // we only cache the user home storage during the before event and then do the
49
+        // action deletion during the after event
50 50
 
51
-		if ($event instanceof BeforeUserDeletedEvent) {
52
-			$userHome = $this->mountProviderCollection->getHomeMountForUser($event->getUser());
53
-			$storage = $userHome->getStorage();
54
-			if (!$storage) {
55
-				throw new \Exception("User has no home storage");
56
-			}
57
-			$this->homeStorageCache[$event->getUser()->getUID()] = $storage;
58
-		}
59
-		if ($event instanceof UserDeletedEvent) {
60
-			if (!isset($this->homeStorageCache[$event->getUser()->getUID()])) {
61
-				throw new \Exception("UserDeletedEvent fired without matching BeforeUserDeletedEvent");
62
-			}
63
-			$storage = $this->homeStorageCache[$event->getUser()->getUID()];
64
-			$cache = $storage->getCache();
65
-			if ($cache instanceof Cache) {
66
-				$cache->clear();
67
-			} else {
68
-				throw new \Exception("Home storage has invalid cache");
69
-			}
70
-			$storage->rmdir('');
71
-		}
72
-	}
51
+        if ($event instanceof BeforeUserDeletedEvent) {
52
+            $userHome = $this->mountProviderCollection->getHomeMountForUser($event->getUser());
53
+            $storage = $userHome->getStorage();
54
+            if (!$storage) {
55
+                throw new \Exception("User has no home storage");
56
+            }
57
+            $this->homeStorageCache[$event->getUser()->getUID()] = $storage;
58
+        }
59
+        if ($event instanceof UserDeletedEvent) {
60
+            if (!isset($this->homeStorageCache[$event->getUser()->getUID()])) {
61
+                throw new \Exception("UserDeletedEvent fired without matching BeforeUserDeletedEvent");
62
+            }
63
+            $storage = $this->homeStorageCache[$event->getUser()->getUID()];
64
+            $cache = $storage->getCache();
65
+            if ($cache instanceof Cache) {
66
+                $cache->clear();
67
+            } else {
68
+                throw new \Exception("Home storage has invalid cache");
69
+            }
70
+            $storage->rmdir('');
71
+        }
72
+    }
73 73
 }
Please login to merge, or discard this patch.
lib/private/User/User.php 2 patches
Indentation   +438 added lines, -438 removed lines patch added patch discarded remove patch
@@ -57,442 +57,442 @@
 block discarded – undo
57 57
 use Symfony\Component\EventDispatcher\GenericEvent;
58 58
 
59 59
 class User implements IUser {
60
-	/** @var string */
61
-	private $uid;
62
-
63
-	/** @var string|null */
64
-	private $displayName;
65
-
66
-	/** @var UserInterface|null */
67
-	private $backend;
68
-	/** @var EventDispatcherInterface */
69
-	private $legacyDispatcher;
70
-
71
-	/** @var IEventDispatcher */
72
-	private $dispatcher;
73
-
74
-	/** @var bool */
75
-	private $enabled;
76
-
77
-	/** @var Emitter|Manager */
78
-	private $emitter;
79
-
80
-	/** @var string */
81
-	private $home;
82
-
83
-	/** @var int */
84
-	private $lastLogin;
85
-
86
-	/** @var \OCP\IConfig */
87
-	private $config;
88
-
89
-	/** @var IAvatarManager */
90
-	private $avatarManager;
91
-
92
-	/** @var IURLGenerator */
93
-	private $urlGenerator;
94
-
95
-	public function __construct(string $uid, ?UserInterface $backend, EventDispatcherInterface $dispatcher, $emitter = null, IConfig $config = null, $urlGenerator = null) {
96
-		$this->uid = $uid;
97
-		$this->backend = $backend;
98
-		$this->legacyDispatcher = $dispatcher;
99
-		$this->emitter = $emitter;
100
-		if (is_null($config)) {
101
-			$config = \OC::$server->getConfig();
102
-		}
103
-		$this->config = $config;
104
-		$this->urlGenerator = $urlGenerator;
105
-		$enabled = $this->config->getUserValue($uid, 'core', 'enabled', 'true');
106
-		$this->enabled = ($enabled === 'true');
107
-		$this->lastLogin = $this->config->getUserValue($uid, 'login', 'lastLogin', 0);
108
-		if (is_null($this->urlGenerator)) {
109
-			$this->urlGenerator = \OC::$server->getURLGenerator();
110
-		}
111
-		// TODO: inject
112
-		$this->dispatcher = \OC::$server->query(IEventDispatcher::class);
113
-	}
114
-
115
-	/**
116
-	 * get the user id
117
-	 *
118
-	 * @return string
119
-	 */
120
-	public function getUID() {
121
-		return $this->uid;
122
-	}
123
-
124
-	/**
125
-	 * get the display name for the user, if no specific display name is set it will fallback to the user id
126
-	 *
127
-	 * @return string
128
-	 */
129
-	public function getDisplayName() {
130
-		if ($this->displayName === null) {
131
-			$displayName = '';
132
-			if ($this->backend && $this->backend->implementsActions(Backend::GET_DISPLAYNAME)) {
133
-				// get display name and strip whitespace from the beginning and end of it
134
-				$backendDisplayName = $this->backend->getDisplayName($this->uid);
135
-				if (is_string($backendDisplayName)) {
136
-					$displayName = trim($backendDisplayName);
137
-				}
138
-			}
139
-
140
-			if (!empty($displayName)) {
141
-				$this->displayName = $displayName;
142
-			} else {
143
-				$this->displayName = $this->uid;
144
-			}
145
-		}
146
-		return $this->displayName;
147
-	}
148
-
149
-	/**
150
-	 * set the displayname for the user
151
-	 *
152
-	 * @param string $displayName
153
-	 * @return bool
154
-	 */
155
-	public function setDisplayName($displayName) {
156
-		$displayName = trim($displayName);
157
-		$oldDisplayName = $this->getDisplayName();
158
-		if ($this->backend->implementsActions(Backend::SET_DISPLAYNAME) && !empty($displayName) && $displayName !== $oldDisplayName) {
159
-			$result = $this->backend->setDisplayName($this->uid, $displayName);
160
-			if ($result) {
161
-				$this->displayName = $displayName;
162
-				$this->triggerChange('displayName', $displayName, $oldDisplayName);
163
-			}
164
-			return $result !== false;
165
-		}
166
-		return false;
167
-	}
168
-
169
-	/**
170
-	 * set the email address of the user
171
-	 *
172
-	 * @param string|null $mailAddress
173
-	 * @return void
174
-	 * @since 9.0.0
175
-	 */
176
-	public function setEMailAddress($mailAddress) {
177
-		$oldMailAddress = $this->getEMailAddress();
178
-		if ($oldMailAddress !== $mailAddress) {
179
-			if ($mailAddress === '') {
180
-				$this->config->deleteUserValue($this->uid, 'settings', 'email');
181
-			} else {
182
-				$this->config->setUserValue($this->uid, 'settings', 'email', $mailAddress);
183
-			}
184
-			$this->triggerChange('eMailAddress', $mailAddress, $oldMailAddress);
185
-		}
186
-	}
187
-
188
-	/**
189
-	 * returns the timestamp of the user's last login or 0 if the user did never
190
-	 * login
191
-	 *
192
-	 * @return int
193
-	 */
194
-	public function getLastLogin() {
195
-		return $this->lastLogin;
196
-	}
197
-
198
-	/**
199
-	 * updates the timestamp of the most recent login of this user
200
-	 */
201
-	public function updateLastLoginTimestamp() {
202
-		$firstTimeLogin = ($this->lastLogin === 0);
203
-		$this->lastLogin = time();
204
-		$this->config->setUserValue(
205
-			$this->uid, 'login', 'lastLogin', $this->lastLogin);
206
-
207
-		return $firstTimeLogin;
208
-	}
209
-
210
-	/**
211
-	 * Delete the user
212
-	 *
213
-	 * @return bool
214
-	 */
215
-	public function delete() {
216
-		/** @deprecated 21.0.0 use BeforeUserDeletedEvent event with the IEventDispatcher instead */
217
-		$this->legacyDispatcher->dispatch(IUser::class . '::preDelete', new GenericEvent($this));
218
-		if ($this->emitter) {
219
-			/** @deprecated 21.0.0 use BeforeUserDeletedEvent event with the IEventDispatcher instead */
220
-			$this->emitter->emit('\OC\User', 'preDelete', [$this]);
221
-		}
222
-		$this->dispatcher->dispatchTyped(new BeforeUserDeletedEvent($this));
223
-		$result = $this->backend->deleteUser($this->uid);
224
-		if ($result) {
225
-
226
-			// FIXME: Feels like an hack - suggestions?
227
-
228
-			$groupManager = \OC::$server->getGroupManager();
229
-			// We have to delete the user from all groups
230
-			foreach ($groupManager->getUserGroupIds($this) as $groupId) {
231
-				$group = $groupManager->get($groupId);
232
-				if ($group) {
233
-					$this->dispatcher->dispatchTyped(new BeforeUserRemovedEvent($group, $this));
234
-					$group->removeUser($this);
235
-					$this->dispatcher->dispatchTyped(new UserRemovedEvent($group, $this));
236
-				}
237
-			}
238
-			// Delete the user's keys in preferences
239
-			\OC::$server->getConfig()->deleteAllUserValues($this->uid);
240
-
241
-			\OC::$server->getCommentsManager()->deleteReferencesOfActor('users', $this->uid);
242
-			\OC::$server->getCommentsManager()->deleteReadMarksFromUser($this);
243
-
244
-			/** @var IAvatarManager $avatarManager */
245
-			$avatarManager = \OC::$server->query(AvatarManager::class);
246
-			$avatarManager->deleteUserAvatar($this->uid);
247
-
248
-			$notification = \OC::$server->getNotificationManager()->createNotification();
249
-			$notification->setUser($this->uid);
250
-			\OC::$server->getNotificationManager()->markProcessed($notification);
251
-
252
-			/** @var AccountManager $accountManager */
253
-			$accountManager = \OC::$server->query(AccountManager::class);
254
-			$accountManager->deleteUser($this);
255
-
256
-			/** @deprecated 21.0.0 use UserDeletedEvent event with the IEventDispatcher instead */
257
-			$this->legacyDispatcher->dispatch(IUser::class . '::postDelete', new GenericEvent($this));
258
-			if ($this->emitter) {
259
-				/** @deprecated 21.0.0 use UserDeletedEvent event with the IEventDispatcher instead */
260
-				$this->emitter->emit('\OC\User', 'postDelete', [$this]);
261
-			}
262
-			$this->dispatcher->dispatchTyped(new UserDeletedEvent($this));
263
-		}
264
-		return !($result === false);
265
-	}
266
-
267
-	/**
268
-	 * Set the password of the user
269
-	 *
270
-	 * @param string $password
271
-	 * @param string $recoveryPassword for the encryption app to reset encryption keys
272
-	 * @return bool
273
-	 */
274
-	public function setPassword($password, $recoveryPassword = null) {
275
-		$this->legacyDispatcher->dispatch(IUser::class . '::preSetPassword', new GenericEvent($this, [
276
-			'password' => $password,
277
-			'recoveryPassword' => $recoveryPassword,
278
-		]));
279
-		if ($this->emitter) {
280
-			$this->emitter->emit('\OC\User', 'preSetPassword', [$this, $password, $recoveryPassword]);
281
-		}
282
-		if ($this->backend->implementsActions(Backend::SET_PASSWORD)) {
283
-			$result = $this->backend->setPassword($this->uid, $password);
284
-			$this->legacyDispatcher->dispatch(IUser::class . '::postSetPassword', new GenericEvent($this, [
285
-				'password' => $password,
286
-				'recoveryPassword' => $recoveryPassword,
287
-			]));
288
-			if ($this->emitter) {
289
-				$this->emitter->emit('\OC\User', 'postSetPassword', [$this, $password, $recoveryPassword]);
290
-			}
291
-			return !($result === false);
292
-		} else {
293
-			return false;
294
-		}
295
-	}
296
-
297
-	/**
298
-	 * get the users home folder to mount
299
-	 *
300
-	 * @return string
301
-	 */
302
-	public function getHome() {
303
-		if (!$this->home) {
304
-			if ($this->backend->implementsActions(Backend::GET_HOME) and $home = $this->backend->getHome($this->uid)) {
305
-				$this->home = $home;
306
-			} elseif ($this->config) {
307
-				$this->home = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $this->uid;
308
-			} else {
309
-				$this->home = \OC::$SERVERROOT . '/data/' . $this->uid;
310
-			}
311
-		}
312
-		return $this->home;
313
-	}
314
-
315
-	/**
316
-	 * Get the name of the backend class the user is connected with
317
-	 *
318
-	 * @return string
319
-	 */
320
-	public function getBackendClassName() {
321
-		if ($this->backend instanceof IUserBackend) {
322
-			return $this->backend->getBackendName();
323
-		}
324
-		return get_class($this->backend);
325
-	}
326
-
327
-	public function getBackend() {
328
-		return $this->backend;
329
-	}
330
-
331
-	/**
332
-	 * check if the backend allows the user to change his avatar on Personal page
333
-	 *
334
-	 * @return bool
335
-	 */
336
-	public function canChangeAvatar() {
337
-		if ($this->backend->implementsActions(Backend::PROVIDE_AVATAR)) {
338
-			return $this->backend->canChangeAvatar($this->uid);
339
-		}
340
-		return true;
341
-	}
342
-
343
-	/**
344
-	 * check if the backend supports changing passwords
345
-	 *
346
-	 * @return bool
347
-	 */
348
-	public function canChangePassword() {
349
-		return $this->backend->implementsActions(Backend::SET_PASSWORD);
350
-	}
351
-
352
-	/**
353
-	 * check if the backend supports changing display names
354
-	 *
355
-	 * @return bool
356
-	 */
357
-	public function canChangeDisplayName() {
358
-		if ($this->config->getSystemValue('allow_user_to_change_display_name') === false) {
359
-			return false;
360
-		}
361
-		return $this->backend->implementsActions(Backend::SET_DISPLAYNAME);
362
-	}
363
-
364
-	/**
365
-	 * check if the user is enabled
366
-	 *
367
-	 * @return bool
368
-	 */
369
-	public function isEnabled() {
370
-		return $this->enabled;
371
-	}
372
-
373
-	/**
374
-	 * set the enabled status for the user
375
-	 *
376
-	 * @param bool $enabled
377
-	 */
378
-	public function setEnabled(bool $enabled = true) {
379
-		$oldStatus = $this->isEnabled();
380
-		$this->enabled = $enabled;
381
-		if ($oldStatus !== $this->enabled) {
382
-			// TODO: First change the value, then trigger the event as done for all other properties.
383
-			$this->triggerChange('enabled', $enabled, $oldStatus);
384
-			$this->config->setUserValue($this->uid, 'core', 'enabled', $enabled ? 'true' : 'false');
385
-		}
386
-	}
387
-
388
-	/**
389
-	 * get the users email address
390
-	 *
391
-	 * @return string|null
392
-	 * @since 9.0.0
393
-	 */
394
-	public function getEMailAddress() {
395
-		return $this->config->getUserValue($this->uid, 'settings', 'email', null);
396
-	}
397
-
398
-	/**
399
-	 * get the users' quota
400
-	 *
401
-	 * @return string
402
-	 * @since 9.0.0
403
-	 */
404
-	public function getQuota() {
405
-		// allow apps to modify the user quota by hooking into the event
406
-		$event = new GetQuotaEvent($this);
407
-		$this->dispatcher->dispatchTyped($event);
408
-		$overwriteQuota = $event->getQuota();
409
-		if ($overwriteQuota) {
410
-			$quota = $overwriteQuota;
411
-		} else {
412
-			$quota = $this->config->getUserValue($this->uid, 'files', 'quota', 'default');
413
-		}
414
-		if ($quota === 'default') {
415
-			$quota = $this->config->getAppValue('files', 'default_quota', 'none');
416
-		}
417
-		return $quota;
418
-	}
419
-
420
-	/**
421
-	 * set the users' quota
422
-	 *
423
-	 * @param string $quota
424
-	 * @return void
425
-	 * @since 9.0.0
426
-	 */
427
-	public function setQuota($quota) {
428
-		$oldQuota = $this->config->getUserValue($this->uid, 'files', 'quota', '');
429
-		if ($quota !== 'none' and $quota !== 'default') {
430
-			$quota = OC_Helper::computerFileSize($quota);
431
-			$quota = OC_Helper::humanFileSize($quota);
432
-		}
433
-		if ($quota !== $oldQuota) {
434
-			$this->config->setUserValue($this->uid, 'files', 'quota', $quota);
435
-			$this->triggerChange('quota', $quota, $oldQuota);
436
-		}
437
-	}
438
-
439
-	/**
440
-	 * get the avatar image if it exists
441
-	 *
442
-	 * @param int $size
443
-	 * @return IImage|null
444
-	 * @since 9.0.0
445
-	 */
446
-	public function getAvatarImage($size) {
447
-		// delay the initialization
448
-		if (is_null($this->avatarManager)) {
449
-			$this->avatarManager = \OC::$server->getAvatarManager();
450
-		}
451
-
452
-		$avatar = $this->avatarManager->getAvatar($this->uid);
453
-		$image = $avatar->get(-1);
454
-		if ($image) {
455
-			return $image;
456
-		}
457
-
458
-		return null;
459
-	}
460
-
461
-	/**
462
-	 * get the federation cloud id
463
-	 *
464
-	 * @return string
465
-	 * @since 9.0.0
466
-	 */
467
-	public function getCloudId() {
468
-		$uid = $this->getUID();
469
-		$server = $this->urlGenerator->getAbsoluteURL('/');
470
-		$server = rtrim($this->removeProtocolFromUrl($server), '/');
471
-		return \OC::$server->getCloudIdManager()->getCloudId($uid, $server)->getId();
472
-	}
473
-
474
-	/**
475
-	 * @param string $url
476
-	 * @return string
477
-	 */
478
-	private function removeProtocolFromUrl($url) {
479
-		if (strpos($url, 'https://') === 0) {
480
-			return substr($url, strlen('https://'));
481
-		} elseif (strpos($url, 'http://') === 0) {
482
-			return substr($url, strlen('http://'));
483
-		}
484
-
485
-		return $url;
486
-	}
487
-
488
-	public function triggerChange($feature, $value = null, $oldValue = null) {
489
-		$this->legacyDispatcher->dispatch(IUser::class . '::changeUser', new GenericEvent($this, [
490
-			'feature' => $feature,
491
-			'value' => $value,
492
-			'oldValue' => $oldValue,
493
-		]));
494
-		if ($this->emitter) {
495
-			$this->emitter->emit('\OC\User', 'changeUser', [$this, $feature, $value, $oldValue]);
496
-		}
497
-	}
60
+    /** @var string */
61
+    private $uid;
62
+
63
+    /** @var string|null */
64
+    private $displayName;
65
+
66
+    /** @var UserInterface|null */
67
+    private $backend;
68
+    /** @var EventDispatcherInterface */
69
+    private $legacyDispatcher;
70
+
71
+    /** @var IEventDispatcher */
72
+    private $dispatcher;
73
+
74
+    /** @var bool */
75
+    private $enabled;
76
+
77
+    /** @var Emitter|Manager */
78
+    private $emitter;
79
+
80
+    /** @var string */
81
+    private $home;
82
+
83
+    /** @var int */
84
+    private $lastLogin;
85
+
86
+    /** @var \OCP\IConfig */
87
+    private $config;
88
+
89
+    /** @var IAvatarManager */
90
+    private $avatarManager;
91
+
92
+    /** @var IURLGenerator */
93
+    private $urlGenerator;
94
+
95
+    public function __construct(string $uid, ?UserInterface $backend, EventDispatcherInterface $dispatcher, $emitter = null, IConfig $config = null, $urlGenerator = null) {
96
+        $this->uid = $uid;
97
+        $this->backend = $backend;
98
+        $this->legacyDispatcher = $dispatcher;
99
+        $this->emitter = $emitter;
100
+        if (is_null($config)) {
101
+            $config = \OC::$server->getConfig();
102
+        }
103
+        $this->config = $config;
104
+        $this->urlGenerator = $urlGenerator;
105
+        $enabled = $this->config->getUserValue($uid, 'core', 'enabled', 'true');
106
+        $this->enabled = ($enabled === 'true');
107
+        $this->lastLogin = $this->config->getUserValue($uid, 'login', 'lastLogin', 0);
108
+        if (is_null($this->urlGenerator)) {
109
+            $this->urlGenerator = \OC::$server->getURLGenerator();
110
+        }
111
+        // TODO: inject
112
+        $this->dispatcher = \OC::$server->query(IEventDispatcher::class);
113
+    }
114
+
115
+    /**
116
+     * get the user id
117
+     *
118
+     * @return string
119
+     */
120
+    public function getUID() {
121
+        return $this->uid;
122
+    }
123
+
124
+    /**
125
+     * get the display name for the user, if no specific display name is set it will fallback to the user id
126
+     *
127
+     * @return string
128
+     */
129
+    public function getDisplayName() {
130
+        if ($this->displayName === null) {
131
+            $displayName = '';
132
+            if ($this->backend && $this->backend->implementsActions(Backend::GET_DISPLAYNAME)) {
133
+                // get display name and strip whitespace from the beginning and end of it
134
+                $backendDisplayName = $this->backend->getDisplayName($this->uid);
135
+                if (is_string($backendDisplayName)) {
136
+                    $displayName = trim($backendDisplayName);
137
+                }
138
+            }
139
+
140
+            if (!empty($displayName)) {
141
+                $this->displayName = $displayName;
142
+            } else {
143
+                $this->displayName = $this->uid;
144
+            }
145
+        }
146
+        return $this->displayName;
147
+    }
148
+
149
+    /**
150
+     * set the displayname for the user
151
+     *
152
+     * @param string $displayName
153
+     * @return bool
154
+     */
155
+    public function setDisplayName($displayName) {
156
+        $displayName = trim($displayName);
157
+        $oldDisplayName = $this->getDisplayName();
158
+        if ($this->backend->implementsActions(Backend::SET_DISPLAYNAME) && !empty($displayName) && $displayName !== $oldDisplayName) {
159
+            $result = $this->backend->setDisplayName($this->uid, $displayName);
160
+            if ($result) {
161
+                $this->displayName = $displayName;
162
+                $this->triggerChange('displayName', $displayName, $oldDisplayName);
163
+            }
164
+            return $result !== false;
165
+        }
166
+        return false;
167
+    }
168
+
169
+    /**
170
+     * set the email address of the user
171
+     *
172
+     * @param string|null $mailAddress
173
+     * @return void
174
+     * @since 9.0.0
175
+     */
176
+    public function setEMailAddress($mailAddress) {
177
+        $oldMailAddress = $this->getEMailAddress();
178
+        if ($oldMailAddress !== $mailAddress) {
179
+            if ($mailAddress === '') {
180
+                $this->config->deleteUserValue($this->uid, 'settings', 'email');
181
+            } else {
182
+                $this->config->setUserValue($this->uid, 'settings', 'email', $mailAddress);
183
+            }
184
+            $this->triggerChange('eMailAddress', $mailAddress, $oldMailAddress);
185
+        }
186
+    }
187
+
188
+    /**
189
+     * returns the timestamp of the user's last login or 0 if the user did never
190
+     * login
191
+     *
192
+     * @return int
193
+     */
194
+    public function getLastLogin() {
195
+        return $this->lastLogin;
196
+    }
197
+
198
+    /**
199
+     * updates the timestamp of the most recent login of this user
200
+     */
201
+    public function updateLastLoginTimestamp() {
202
+        $firstTimeLogin = ($this->lastLogin === 0);
203
+        $this->lastLogin = time();
204
+        $this->config->setUserValue(
205
+            $this->uid, 'login', 'lastLogin', $this->lastLogin);
206
+
207
+        return $firstTimeLogin;
208
+    }
209
+
210
+    /**
211
+     * Delete the user
212
+     *
213
+     * @return bool
214
+     */
215
+    public function delete() {
216
+        /** @deprecated 21.0.0 use BeforeUserDeletedEvent event with the IEventDispatcher instead */
217
+        $this->legacyDispatcher->dispatch(IUser::class . '::preDelete', new GenericEvent($this));
218
+        if ($this->emitter) {
219
+            /** @deprecated 21.0.0 use BeforeUserDeletedEvent event with the IEventDispatcher instead */
220
+            $this->emitter->emit('\OC\User', 'preDelete', [$this]);
221
+        }
222
+        $this->dispatcher->dispatchTyped(new BeforeUserDeletedEvent($this));
223
+        $result = $this->backend->deleteUser($this->uid);
224
+        if ($result) {
225
+
226
+            // FIXME: Feels like an hack - suggestions?
227
+
228
+            $groupManager = \OC::$server->getGroupManager();
229
+            // We have to delete the user from all groups
230
+            foreach ($groupManager->getUserGroupIds($this) as $groupId) {
231
+                $group = $groupManager->get($groupId);
232
+                if ($group) {
233
+                    $this->dispatcher->dispatchTyped(new BeforeUserRemovedEvent($group, $this));
234
+                    $group->removeUser($this);
235
+                    $this->dispatcher->dispatchTyped(new UserRemovedEvent($group, $this));
236
+                }
237
+            }
238
+            // Delete the user's keys in preferences
239
+            \OC::$server->getConfig()->deleteAllUserValues($this->uid);
240
+
241
+            \OC::$server->getCommentsManager()->deleteReferencesOfActor('users', $this->uid);
242
+            \OC::$server->getCommentsManager()->deleteReadMarksFromUser($this);
243
+
244
+            /** @var IAvatarManager $avatarManager */
245
+            $avatarManager = \OC::$server->query(AvatarManager::class);
246
+            $avatarManager->deleteUserAvatar($this->uid);
247
+
248
+            $notification = \OC::$server->getNotificationManager()->createNotification();
249
+            $notification->setUser($this->uid);
250
+            \OC::$server->getNotificationManager()->markProcessed($notification);
251
+
252
+            /** @var AccountManager $accountManager */
253
+            $accountManager = \OC::$server->query(AccountManager::class);
254
+            $accountManager->deleteUser($this);
255
+
256
+            /** @deprecated 21.0.0 use UserDeletedEvent event with the IEventDispatcher instead */
257
+            $this->legacyDispatcher->dispatch(IUser::class . '::postDelete', new GenericEvent($this));
258
+            if ($this->emitter) {
259
+                /** @deprecated 21.0.0 use UserDeletedEvent event with the IEventDispatcher instead */
260
+                $this->emitter->emit('\OC\User', 'postDelete', [$this]);
261
+            }
262
+            $this->dispatcher->dispatchTyped(new UserDeletedEvent($this));
263
+        }
264
+        return !($result === false);
265
+    }
266
+
267
+    /**
268
+     * Set the password of the user
269
+     *
270
+     * @param string $password
271
+     * @param string $recoveryPassword for the encryption app to reset encryption keys
272
+     * @return bool
273
+     */
274
+    public function setPassword($password, $recoveryPassword = null) {
275
+        $this->legacyDispatcher->dispatch(IUser::class . '::preSetPassword', new GenericEvent($this, [
276
+            'password' => $password,
277
+            'recoveryPassword' => $recoveryPassword,
278
+        ]));
279
+        if ($this->emitter) {
280
+            $this->emitter->emit('\OC\User', 'preSetPassword', [$this, $password, $recoveryPassword]);
281
+        }
282
+        if ($this->backend->implementsActions(Backend::SET_PASSWORD)) {
283
+            $result = $this->backend->setPassword($this->uid, $password);
284
+            $this->legacyDispatcher->dispatch(IUser::class . '::postSetPassword', new GenericEvent($this, [
285
+                'password' => $password,
286
+                'recoveryPassword' => $recoveryPassword,
287
+            ]));
288
+            if ($this->emitter) {
289
+                $this->emitter->emit('\OC\User', 'postSetPassword', [$this, $password, $recoveryPassword]);
290
+            }
291
+            return !($result === false);
292
+        } else {
293
+            return false;
294
+        }
295
+    }
296
+
297
+    /**
298
+     * get the users home folder to mount
299
+     *
300
+     * @return string
301
+     */
302
+    public function getHome() {
303
+        if (!$this->home) {
304
+            if ($this->backend->implementsActions(Backend::GET_HOME) and $home = $this->backend->getHome($this->uid)) {
305
+                $this->home = $home;
306
+            } elseif ($this->config) {
307
+                $this->home = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $this->uid;
308
+            } else {
309
+                $this->home = \OC::$SERVERROOT . '/data/' . $this->uid;
310
+            }
311
+        }
312
+        return $this->home;
313
+    }
314
+
315
+    /**
316
+     * Get the name of the backend class the user is connected with
317
+     *
318
+     * @return string
319
+     */
320
+    public function getBackendClassName() {
321
+        if ($this->backend instanceof IUserBackend) {
322
+            return $this->backend->getBackendName();
323
+        }
324
+        return get_class($this->backend);
325
+    }
326
+
327
+    public function getBackend() {
328
+        return $this->backend;
329
+    }
330
+
331
+    /**
332
+     * check if the backend allows the user to change his avatar on Personal page
333
+     *
334
+     * @return bool
335
+     */
336
+    public function canChangeAvatar() {
337
+        if ($this->backend->implementsActions(Backend::PROVIDE_AVATAR)) {
338
+            return $this->backend->canChangeAvatar($this->uid);
339
+        }
340
+        return true;
341
+    }
342
+
343
+    /**
344
+     * check if the backend supports changing passwords
345
+     *
346
+     * @return bool
347
+     */
348
+    public function canChangePassword() {
349
+        return $this->backend->implementsActions(Backend::SET_PASSWORD);
350
+    }
351
+
352
+    /**
353
+     * check if the backend supports changing display names
354
+     *
355
+     * @return bool
356
+     */
357
+    public function canChangeDisplayName() {
358
+        if ($this->config->getSystemValue('allow_user_to_change_display_name') === false) {
359
+            return false;
360
+        }
361
+        return $this->backend->implementsActions(Backend::SET_DISPLAYNAME);
362
+    }
363
+
364
+    /**
365
+     * check if the user is enabled
366
+     *
367
+     * @return bool
368
+     */
369
+    public function isEnabled() {
370
+        return $this->enabled;
371
+    }
372
+
373
+    /**
374
+     * set the enabled status for the user
375
+     *
376
+     * @param bool $enabled
377
+     */
378
+    public function setEnabled(bool $enabled = true) {
379
+        $oldStatus = $this->isEnabled();
380
+        $this->enabled = $enabled;
381
+        if ($oldStatus !== $this->enabled) {
382
+            // TODO: First change the value, then trigger the event as done for all other properties.
383
+            $this->triggerChange('enabled', $enabled, $oldStatus);
384
+            $this->config->setUserValue($this->uid, 'core', 'enabled', $enabled ? 'true' : 'false');
385
+        }
386
+    }
387
+
388
+    /**
389
+     * get the users email address
390
+     *
391
+     * @return string|null
392
+     * @since 9.0.0
393
+     */
394
+    public function getEMailAddress() {
395
+        return $this->config->getUserValue($this->uid, 'settings', 'email', null);
396
+    }
397
+
398
+    /**
399
+     * get the users' quota
400
+     *
401
+     * @return string
402
+     * @since 9.0.0
403
+     */
404
+    public function getQuota() {
405
+        // allow apps to modify the user quota by hooking into the event
406
+        $event = new GetQuotaEvent($this);
407
+        $this->dispatcher->dispatchTyped($event);
408
+        $overwriteQuota = $event->getQuota();
409
+        if ($overwriteQuota) {
410
+            $quota = $overwriteQuota;
411
+        } else {
412
+            $quota = $this->config->getUserValue($this->uid, 'files', 'quota', 'default');
413
+        }
414
+        if ($quota === 'default') {
415
+            $quota = $this->config->getAppValue('files', 'default_quota', 'none');
416
+        }
417
+        return $quota;
418
+    }
419
+
420
+    /**
421
+     * set the users' quota
422
+     *
423
+     * @param string $quota
424
+     * @return void
425
+     * @since 9.0.0
426
+     */
427
+    public function setQuota($quota) {
428
+        $oldQuota = $this->config->getUserValue($this->uid, 'files', 'quota', '');
429
+        if ($quota !== 'none' and $quota !== 'default') {
430
+            $quota = OC_Helper::computerFileSize($quota);
431
+            $quota = OC_Helper::humanFileSize($quota);
432
+        }
433
+        if ($quota !== $oldQuota) {
434
+            $this->config->setUserValue($this->uid, 'files', 'quota', $quota);
435
+            $this->triggerChange('quota', $quota, $oldQuota);
436
+        }
437
+    }
438
+
439
+    /**
440
+     * get the avatar image if it exists
441
+     *
442
+     * @param int $size
443
+     * @return IImage|null
444
+     * @since 9.0.0
445
+     */
446
+    public function getAvatarImage($size) {
447
+        // delay the initialization
448
+        if (is_null($this->avatarManager)) {
449
+            $this->avatarManager = \OC::$server->getAvatarManager();
450
+        }
451
+
452
+        $avatar = $this->avatarManager->getAvatar($this->uid);
453
+        $image = $avatar->get(-1);
454
+        if ($image) {
455
+            return $image;
456
+        }
457
+
458
+        return null;
459
+    }
460
+
461
+    /**
462
+     * get the federation cloud id
463
+     *
464
+     * @return string
465
+     * @since 9.0.0
466
+     */
467
+    public function getCloudId() {
468
+        $uid = $this->getUID();
469
+        $server = $this->urlGenerator->getAbsoluteURL('/');
470
+        $server = rtrim($this->removeProtocolFromUrl($server), '/');
471
+        return \OC::$server->getCloudIdManager()->getCloudId($uid, $server)->getId();
472
+    }
473
+
474
+    /**
475
+     * @param string $url
476
+     * @return string
477
+     */
478
+    private function removeProtocolFromUrl($url) {
479
+        if (strpos($url, 'https://') === 0) {
480
+            return substr($url, strlen('https://'));
481
+        } elseif (strpos($url, 'http://') === 0) {
482
+            return substr($url, strlen('http://'));
483
+        }
484
+
485
+        return $url;
486
+    }
487
+
488
+    public function triggerChange($feature, $value = null, $oldValue = null) {
489
+        $this->legacyDispatcher->dispatch(IUser::class . '::changeUser', new GenericEvent($this, [
490
+            'feature' => $feature,
491
+            'value' => $value,
492
+            'oldValue' => $oldValue,
493
+        ]));
494
+        if ($this->emitter) {
495
+            $this->emitter->emit('\OC\User', 'changeUser', [$this, $feature, $value, $oldValue]);
496
+        }
497
+    }
498 498
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -214,7 +214,7 @@  discard block
 block discarded – undo
214 214
 	 */
215 215
 	public function delete() {
216 216
 		/** @deprecated 21.0.0 use BeforeUserDeletedEvent event with the IEventDispatcher instead */
217
-		$this->legacyDispatcher->dispatch(IUser::class . '::preDelete', new GenericEvent($this));
217
+		$this->legacyDispatcher->dispatch(IUser::class.'::preDelete', new GenericEvent($this));
218 218
 		if ($this->emitter) {
219 219
 			/** @deprecated 21.0.0 use BeforeUserDeletedEvent event with the IEventDispatcher instead */
220 220
 			$this->emitter->emit('\OC\User', 'preDelete', [$this]);
@@ -254,7 +254,7 @@  discard block
 block discarded – undo
254 254
 			$accountManager->deleteUser($this);
255 255
 
256 256
 			/** @deprecated 21.0.0 use UserDeletedEvent event with the IEventDispatcher instead */
257
-			$this->legacyDispatcher->dispatch(IUser::class . '::postDelete', new GenericEvent($this));
257
+			$this->legacyDispatcher->dispatch(IUser::class.'::postDelete', new GenericEvent($this));
258 258
 			if ($this->emitter) {
259 259
 				/** @deprecated 21.0.0 use UserDeletedEvent event with the IEventDispatcher instead */
260 260
 				$this->emitter->emit('\OC\User', 'postDelete', [$this]);
@@ -272,7 +272,7 @@  discard block
 block discarded – undo
272 272
 	 * @return bool
273 273
 	 */
274 274
 	public function setPassword($password, $recoveryPassword = null) {
275
-		$this->legacyDispatcher->dispatch(IUser::class . '::preSetPassword', new GenericEvent($this, [
275
+		$this->legacyDispatcher->dispatch(IUser::class.'::preSetPassword', new GenericEvent($this, [
276 276
 			'password' => $password,
277 277
 			'recoveryPassword' => $recoveryPassword,
278 278
 		]));
@@ -281,7 +281,7 @@  discard block
 block discarded – undo
281 281
 		}
282 282
 		if ($this->backend->implementsActions(Backend::SET_PASSWORD)) {
283 283
 			$result = $this->backend->setPassword($this->uid, $password);
284
-			$this->legacyDispatcher->dispatch(IUser::class . '::postSetPassword', new GenericEvent($this, [
284
+			$this->legacyDispatcher->dispatch(IUser::class.'::postSetPassword', new GenericEvent($this, [
285 285
 				'password' => $password,
286 286
 				'recoveryPassword' => $recoveryPassword,
287 287
 			]));
@@ -304,9 +304,9 @@  discard block
 block discarded – undo
304 304
 			if ($this->backend->implementsActions(Backend::GET_HOME) and $home = $this->backend->getHome($this->uid)) {
305 305
 				$this->home = $home;
306 306
 			} elseif ($this->config) {
307
-				$this->home = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/' . $this->uid;
307
+				$this->home = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT.'/data').'/'.$this->uid;
308 308
 			} else {
309
-				$this->home = \OC::$SERVERROOT . '/data/' . $this->uid;
309
+				$this->home = \OC::$SERVERROOT.'/data/'.$this->uid;
310 310
 			}
311 311
 		}
312 312
 		return $this->home;
@@ -486,7 +486,7 @@  discard block
 block discarded – undo
486 486
 	}
487 487
 
488 488
 	public function triggerChange($feature, $value = null, $oldValue = null) {
489
-		$this->legacyDispatcher->dispatch(IUser::class . '::changeUser', new GenericEvent($this, [
489
+		$this->legacyDispatcher->dispatch(IUser::class.'::changeUser', new GenericEvent($this, [
490 490
 			'feature' => $feature,
491 491
 			'value' => $value,
492 492
 			'oldValue' => $oldValue,
Please login to merge, or discard this patch.
lib/private/Files/Storage/Common.php 1 patch
Indentation   +809 added lines, -809 removed lines patch added patch discarded remove patch
@@ -76,817 +76,817 @@
 block discarded – undo
76 76
  * in classes which extend it, e.g. $this->stat() .
77 77
  */
78 78
 abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage {
79
-	use LocalTempFileTrait;
80
-
81
-	protected $cache;
82
-	protected $scanner;
83
-	protected $watcher;
84
-	protected $propagator;
85
-	protected $storageCache;
86
-	protected $updater;
87
-
88
-	protected $mountOptions = [];
89
-	protected $owner = null;
90
-
91
-	private $shouldLogLocks = null;
92
-	private $logger;
93
-
94
-	public function __construct($parameters) {
95
-	}
96
-
97
-	/**
98
-	 * Remove a file or folder
99
-	 *
100
-	 * @param string $path
101
-	 * @return bool
102
-	 */
103
-	protected function remove($path) {
104
-		if ($this->is_dir($path)) {
105
-			return $this->rmdir($path);
106
-		} elseif ($this->is_file($path)) {
107
-			return $this->unlink($path);
108
-		} else {
109
-			return false;
110
-		}
111
-	}
112
-
113
-	public function is_dir($path) {
114
-		return $this->filetype($path) === 'dir';
115
-	}
116
-
117
-	public function is_file($path) {
118
-		return $this->filetype($path) === 'file';
119
-	}
120
-
121
-	public function filesize($path) {
122
-		if ($this->is_dir($path)) {
123
-			return 0; //by definition
124
-		} else {
125
-			$stat = $this->stat($path);
126
-			if (isset($stat['size'])) {
127
-				return $stat['size'];
128
-			} else {
129
-				return 0;
130
-			}
131
-		}
132
-	}
133
-
134
-	public function isReadable($path) {
135
-		// at least check whether it exists
136
-		// subclasses might want to implement this more thoroughly
137
-		return $this->file_exists($path);
138
-	}
139
-
140
-	public function isUpdatable($path) {
141
-		// at least check whether it exists
142
-		// subclasses might want to implement this more thoroughly
143
-		// a non-existing file/folder isn't updatable
144
-		return $this->file_exists($path);
145
-	}
146
-
147
-	public function isCreatable($path) {
148
-		if ($this->is_dir($path) && $this->isUpdatable($path)) {
149
-			return true;
150
-		}
151
-		return false;
152
-	}
153
-
154
-	public function isDeletable($path) {
155
-		if ($path === '' || $path === '/') {
156
-			return $this->isUpdatable($path);
157
-		}
158
-		$parent = dirname($path);
159
-		return $this->isUpdatable($parent) && $this->isUpdatable($path);
160
-	}
161
-
162
-	public function isSharable($path) {
163
-		return $this->isReadable($path);
164
-	}
165
-
166
-	public function getPermissions($path) {
167
-		$permissions = 0;
168
-		if ($this->isCreatable($path)) {
169
-			$permissions |= \OCP\Constants::PERMISSION_CREATE;
170
-		}
171
-		if ($this->isReadable($path)) {
172
-			$permissions |= \OCP\Constants::PERMISSION_READ;
173
-		}
174
-		if ($this->isUpdatable($path)) {
175
-			$permissions |= \OCP\Constants::PERMISSION_UPDATE;
176
-		}
177
-		if ($this->isDeletable($path)) {
178
-			$permissions |= \OCP\Constants::PERMISSION_DELETE;
179
-		}
180
-		if ($this->isSharable($path)) {
181
-			$permissions |= \OCP\Constants::PERMISSION_SHARE;
182
-		}
183
-		return $permissions;
184
-	}
185
-
186
-	public function filemtime($path) {
187
-		$stat = $this->stat($path);
188
-		if (isset($stat['mtime']) && $stat['mtime'] > 0) {
189
-			return $stat['mtime'];
190
-		} else {
191
-			return 0;
192
-		}
193
-	}
194
-
195
-	public function file_get_contents($path) {
196
-		$handle = $this->fopen($path, "r");
197
-		if (!$handle) {
198
-			return false;
199
-		}
200
-		$data = stream_get_contents($handle);
201
-		fclose($handle);
202
-		return $data;
203
-	}
204
-
205
-	public function file_put_contents($path, $data) {
206
-		$handle = $this->fopen($path, "w");
207
-		$this->removeCachedFile($path);
208
-		$count = fwrite($handle, $data);
209
-		fclose($handle);
210
-		return $count;
211
-	}
212
-
213
-	public function rename($path1, $path2) {
214
-		$this->remove($path2);
215
-
216
-		$this->removeCachedFile($path1);
217
-		return $this->copy($path1, $path2) and $this->remove($path1);
218
-	}
219
-
220
-	public function copy($path1, $path2) {
221
-		if ($this->is_dir($path1)) {
222
-			$this->remove($path2);
223
-			$dir = $this->opendir($path1);
224
-			$this->mkdir($path2);
225
-			while ($file = readdir($dir)) {
226
-				if (!Filesystem::isIgnoredDir($file)) {
227
-					if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
228
-						return false;
229
-					}
230
-				}
231
-			}
232
-			closedir($dir);
233
-			return true;
234
-		} else {
235
-			$source = $this->fopen($path1, 'r');
236
-			$target = $this->fopen($path2, 'w');
237
-			[, $result] = \OC_Helper::streamCopy($source, $target);
238
-			if (!$result) {
239
-				\OC::$server->getLogger()->warning("Failed to write data while copying $path1 to $path2");
240
-			}
241
-			$this->removeCachedFile($path2);
242
-			return $result;
243
-		}
244
-	}
245
-
246
-	public function getMimeType($path) {
247
-		if ($this->is_dir($path)) {
248
-			return 'httpd/unix-directory';
249
-		} elseif ($this->file_exists($path)) {
250
-			return \OC::$server->getMimeTypeDetector()->detectPath($path);
251
-		} else {
252
-			return false;
253
-		}
254
-	}
255
-
256
-	public function hash($type, $path, $raw = false) {
257
-		$fh = $this->fopen($path, 'rb');
258
-		$ctx = hash_init($type);
259
-		hash_update_stream($ctx, $fh);
260
-		fclose($fh);
261
-		return hash_final($ctx, $raw);
262
-	}
263
-
264
-	public function search($query) {
265
-		return $this->searchInDir($query);
266
-	}
267
-
268
-	public function getLocalFile($path) {
269
-		return $this->getCachedFile($path);
270
-	}
271
-
272
-	/**
273
-	 * @param string $path
274
-	 * @param string $target
275
-	 */
276
-	private function addLocalFolder($path, $target) {
277
-		$dh = $this->opendir($path);
278
-		if (is_resource($dh)) {
279
-			while (($file = readdir($dh)) !== false) {
280
-				if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
281
-					if ($this->is_dir($path . '/' . $file)) {
282
-						mkdir($target . '/' . $file);
283
-						$this->addLocalFolder($path . '/' . $file, $target . '/' . $file);
284
-					} else {
285
-						$tmp = $this->toTmpFile($path . '/' . $file);
286
-						rename($tmp, $target . '/' . $file);
287
-					}
288
-				}
289
-			}
290
-		}
291
-	}
292
-
293
-	/**
294
-	 * @param string $query
295
-	 * @param string $dir
296
-	 * @return array
297
-	 */
298
-	protected function searchInDir($query, $dir = '') {
299
-		$files = [];
300
-		$dh = $this->opendir($dir);
301
-		if (is_resource($dh)) {
302
-			while (($item = readdir($dh)) !== false) {
303
-				if (\OC\Files\Filesystem::isIgnoredDir($item)) {
304
-					continue;
305
-				}
306
-				if (strstr(strtolower($item), strtolower($query)) !== false) {
307
-					$files[] = $dir . '/' . $item;
308
-				}
309
-				if ($this->is_dir($dir . '/' . $item)) {
310
-					$files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
311
-				}
312
-			}
313
-		}
314
-		closedir($dh);
315
-		return $files;
316
-	}
317
-
318
-	/**
319
-	 * check if a file or folder has been updated since $time
320
-	 *
321
-	 * The method is only used to check if the cache needs to be updated. Storage backends that don't support checking
322
-	 * the mtime should always return false here. As a result storage implementations that always return false expect
323
-	 * exclusive access to the backend and will not pick up files that have been added in a way that circumvents
324
-	 * ownClouds filesystem.
325
-	 *
326
-	 * @param string $path
327
-	 * @param int $time
328
-	 * @return bool
329
-	 */
330
-	public function hasUpdated($path, $time) {
331
-		return $this->filemtime($path) > $time;
332
-	}
333
-
334
-	public function getCache($path = '', $storage = null) {
335
-		if (!$storage) {
336
-			$storage = $this;
337
-		}
338
-		if (!isset($storage->cache)) {
339
-			$storage->cache = new Cache($storage);
340
-		}
341
-		return $storage->cache;
342
-	}
343
-
344
-	public function getScanner($path = '', $storage = null) {
345
-		if (!$storage) {
346
-			$storage = $this;
347
-		}
348
-		if (!isset($storage->scanner)) {
349
-			$storage->scanner = new Scanner($storage);
350
-		}
351
-		return $storage->scanner;
352
-	}
353
-
354
-	public function getWatcher($path = '', $storage = null) {
355
-		if (!$storage) {
356
-			$storage = $this;
357
-		}
358
-		if (!isset($this->watcher)) {
359
-			$this->watcher = new Watcher($storage);
360
-			$globalPolicy = \OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_NEVER);
361
-			$this->watcher->setPolicy((int)$this->getMountOption('filesystem_check_changes', $globalPolicy));
362
-		}
363
-		return $this->watcher;
364
-	}
365
-
366
-	/**
367
-	 * get a propagator instance for the cache
368
-	 *
369
-	 * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
370
-	 * @return \OC\Files\Cache\Propagator
371
-	 */
372
-	public function getPropagator($storage = null) {
373
-		if (!$storage) {
374
-			$storage = $this;
375
-		}
376
-		if (!isset($storage->propagator)) {
377
-			$config = \OC::$server->getSystemConfig();
378
-			$storage->propagator = new Propagator($storage, \OC::$server->getDatabaseConnection(), ['appdata_' . $config->getValue('instanceid')]);
379
-		}
380
-		return $storage->propagator;
381
-	}
382
-
383
-	public function getUpdater($storage = null) {
384
-		if (!$storage) {
385
-			$storage = $this;
386
-		}
387
-		if (!isset($storage->updater)) {
388
-			$storage->updater = new Updater($storage);
389
-		}
390
-		return $storage->updater;
391
-	}
392
-
393
-	public function getStorageCache($storage = null) {
394
-		if (!$storage) {
395
-			$storage = $this;
396
-		}
397
-		if (!isset($this->storageCache)) {
398
-			$this->storageCache = new \OC\Files\Cache\Storage($storage);
399
-		}
400
-		return $this->storageCache;
401
-	}
402
-
403
-	/**
404
-	 * get the owner of a path
405
-	 *
406
-	 * @param string $path The path to get the owner
407
-	 * @return string|false uid or false
408
-	 */
409
-	public function getOwner($path) {
410
-		if ($this->owner === null) {
411
-			$this->owner = \OC_User::getUser();
412
-		}
413
-
414
-		return $this->owner;
415
-	}
416
-
417
-	/**
418
-	 * get the ETag for a file or folder
419
-	 *
420
-	 * @param string $path
421
-	 * @return string
422
-	 */
423
-	public function getETag($path) {
424
-		return uniqid();
425
-	}
426
-
427
-	/**
428
-	 * clean a path, i.e. remove all redundant '.' and '..'
429
-	 * making sure that it can't point to higher than '/'
430
-	 *
431
-	 * @param string $path The path to clean
432
-	 * @return string cleaned path
433
-	 */
434
-	public function cleanPath($path) {
435
-		if (strlen($path) == 0 or $path[0] != '/') {
436
-			$path = '/' . $path;
437
-		}
438
-
439
-		$output = [];
440
-		foreach (explode('/', $path) as $chunk) {
441
-			if ($chunk == '..') {
442
-				array_pop($output);
443
-			} elseif ($chunk == '.') {
444
-			} else {
445
-				$output[] = $chunk;
446
-			}
447
-		}
448
-		return implode('/', $output);
449
-	}
450
-
451
-	/**
452
-	 * Test a storage for availability
453
-	 *
454
-	 * @return bool
455
-	 */
456
-	public function test() {
457
-		try {
458
-			if ($this->stat('')) {
459
-				return true;
460
-			}
461
-			\OC::$server->getLogger()->info("External storage not available: stat() failed");
462
-			return false;
463
-		} catch (\Exception $e) {
464
-			\OC::$server->getLogger()->warning("External storage not available: " . $e->getMessage());
465
-			\OC::$server->getLogger()->logException($e, ['level' => ILogger::WARN]);
466
-			return false;
467
-		}
468
-	}
469
-
470
-	/**
471
-	 * get the free space in the storage
472
-	 *
473
-	 * @param string $path
474
-	 * @return int|false
475
-	 */
476
-	public function free_space($path) {
477
-		return \OCP\Files\FileInfo::SPACE_UNKNOWN;
478
-	}
479
-
480
-	/**
481
-	 * {@inheritdoc}
482
-	 */
483
-	public function isLocal() {
484
-		// the common implementation returns a temporary file by
485
-		// default, which is not local
486
-		return false;
487
-	}
488
-
489
-	/**
490
-	 * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class
491
-	 *
492
-	 * @param string $class
493
-	 * @return bool
494
-	 */
495
-	public function instanceOfStorage($class) {
496
-		if (ltrim($class, '\\') === 'OC\Files\Storage\Shared') {
497
-			// FIXME Temporary fix to keep existing checks working
498
-			$class = '\OCA\Files_Sharing\SharedStorage';
499
-		}
500
-		return is_a($this, $class);
501
-	}
502
-
503
-	/**
504
-	 * A custom storage implementation can return an url for direct download of a give file.
505
-	 *
506
-	 * For now the returned array can hold the parameter url - in future more attributes might follow.
507
-	 *
508
-	 * @param string $path
509
-	 * @return array|false
510
-	 */
511
-	public function getDirectDownload($path) {
512
-		return [];
513
-	}
514
-
515
-	/**
516
-	 * @inheritdoc
517
-	 * @throws InvalidPathException
518
-	 */
519
-	public function verifyPath($path, $fileName) {
520
-
521
-		// verify empty and dot files
522
-		$trimmed = trim($fileName);
523
-		if ($trimmed === '') {
524
-			throw new EmptyFileNameException();
525
-		}
526
-
527
-		if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
528
-			throw new InvalidDirectoryException();
529
-		}
530
-
531
-		if (!\OC::$server->getDatabaseConnection()->supports4ByteText()) {
532
-			// verify database - e.g. mysql only 3-byte chars
533
-			if (preg_match('%(?:
79
+    use LocalTempFileTrait;
80
+
81
+    protected $cache;
82
+    protected $scanner;
83
+    protected $watcher;
84
+    protected $propagator;
85
+    protected $storageCache;
86
+    protected $updater;
87
+
88
+    protected $mountOptions = [];
89
+    protected $owner = null;
90
+
91
+    private $shouldLogLocks = null;
92
+    private $logger;
93
+
94
+    public function __construct($parameters) {
95
+    }
96
+
97
+    /**
98
+     * Remove a file or folder
99
+     *
100
+     * @param string $path
101
+     * @return bool
102
+     */
103
+    protected function remove($path) {
104
+        if ($this->is_dir($path)) {
105
+            return $this->rmdir($path);
106
+        } elseif ($this->is_file($path)) {
107
+            return $this->unlink($path);
108
+        } else {
109
+            return false;
110
+        }
111
+    }
112
+
113
+    public function is_dir($path) {
114
+        return $this->filetype($path) === 'dir';
115
+    }
116
+
117
+    public function is_file($path) {
118
+        return $this->filetype($path) === 'file';
119
+    }
120
+
121
+    public function filesize($path) {
122
+        if ($this->is_dir($path)) {
123
+            return 0; //by definition
124
+        } else {
125
+            $stat = $this->stat($path);
126
+            if (isset($stat['size'])) {
127
+                return $stat['size'];
128
+            } else {
129
+                return 0;
130
+            }
131
+        }
132
+    }
133
+
134
+    public function isReadable($path) {
135
+        // at least check whether it exists
136
+        // subclasses might want to implement this more thoroughly
137
+        return $this->file_exists($path);
138
+    }
139
+
140
+    public function isUpdatable($path) {
141
+        // at least check whether it exists
142
+        // subclasses might want to implement this more thoroughly
143
+        // a non-existing file/folder isn't updatable
144
+        return $this->file_exists($path);
145
+    }
146
+
147
+    public function isCreatable($path) {
148
+        if ($this->is_dir($path) && $this->isUpdatable($path)) {
149
+            return true;
150
+        }
151
+        return false;
152
+    }
153
+
154
+    public function isDeletable($path) {
155
+        if ($path === '' || $path === '/') {
156
+            return $this->isUpdatable($path);
157
+        }
158
+        $parent = dirname($path);
159
+        return $this->isUpdatable($parent) && $this->isUpdatable($path);
160
+    }
161
+
162
+    public function isSharable($path) {
163
+        return $this->isReadable($path);
164
+    }
165
+
166
+    public function getPermissions($path) {
167
+        $permissions = 0;
168
+        if ($this->isCreatable($path)) {
169
+            $permissions |= \OCP\Constants::PERMISSION_CREATE;
170
+        }
171
+        if ($this->isReadable($path)) {
172
+            $permissions |= \OCP\Constants::PERMISSION_READ;
173
+        }
174
+        if ($this->isUpdatable($path)) {
175
+            $permissions |= \OCP\Constants::PERMISSION_UPDATE;
176
+        }
177
+        if ($this->isDeletable($path)) {
178
+            $permissions |= \OCP\Constants::PERMISSION_DELETE;
179
+        }
180
+        if ($this->isSharable($path)) {
181
+            $permissions |= \OCP\Constants::PERMISSION_SHARE;
182
+        }
183
+        return $permissions;
184
+    }
185
+
186
+    public function filemtime($path) {
187
+        $stat = $this->stat($path);
188
+        if (isset($stat['mtime']) && $stat['mtime'] > 0) {
189
+            return $stat['mtime'];
190
+        } else {
191
+            return 0;
192
+        }
193
+    }
194
+
195
+    public function file_get_contents($path) {
196
+        $handle = $this->fopen($path, "r");
197
+        if (!$handle) {
198
+            return false;
199
+        }
200
+        $data = stream_get_contents($handle);
201
+        fclose($handle);
202
+        return $data;
203
+    }
204
+
205
+    public function file_put_contents($path, $data) {
206
+        $handle = $this->fopen($path, "w");
207
+        $this->removeCachedFile($path);
208
+        $count = fwrite($handle, $data);
209
+        fclose($handle);
210
+        return $count;
211
+    }
212
+
213
+    public function rename($path1, $path2) {
214
+        $this->remove($path2);
215
+
216
+        $this->removeCachedFile($path1);
217
+        return $this->copy($path1, $path2) and $this->remove($path1);
218
+    }
219
+
220
+    public function copy($path1, $path2) {
221
+        if ($this->is_dir($path1)) {
222
+            $this->remove($path2);
223
+            $dir = $this->opendir($path1);
224
+            $this->mkdir($path2);
225
+            while ($file = readdir($dir)) {
226
+                if (!Filesystem::isIgnoredDir($file)) {
227
+                    if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file)) {
228
+                        return false;
229
+                    }
230
+                }
231
+            }
232
+            closedir($dir);
233
+            return true;
234
+        } else {
235
+            $source = $this->fopen($path1, 'r');
236
+            $target = $this->fopen($path2, 'w');
237
+            [, $result] = \OC_Helper::streamCopy($source, $target);
238
+            if (!$result) {
239
+                \OC::$server->getLogger()->warning("Failed to write data while copying $path1 to $path2");
240
+            }
241
+            $this->removeCachedFile($path2);
242
+            return $result;
243
+        }
244
+    }
245
+
246
+    public function getMimeType($path) {
247
+        if ($this->is_dir($path)) {
248
+            return 'httpd/unix-directory';
249
+        } elseif ($this->file_exists($path)) {
250
+            return \OC::$server->getMimeTypeDetector()->detectPath($path);
251
+        } else {
252
+            return false;
253
+        }
254
+    }
255
+
256
+    public function hash($type, $path, $raw = false) {
257
+        $fh = $this->fopen($path, 'rb');
258
+        $ctx = hash_init($type);
259
+        hash_update_stream($ctx, $fh);
260
+        fclose($fh);
261
+        return hash_final($ctx, $raw);
262
+    }
263
+
264
+    public function search($query) {
265
+        return $this->searchInDir($query);
266
+    }
267
+
268
+    public function getLocalFile($path) {
269
+        return $this->getCachedFile($path);
270
+    }
271
+
272
+    /**
273
+     * @param string $path
274
+     * @param string $target
275
+     */
276
+    private function addLocalFolder($path, $target) {
277
+        $dh = $this->opendir($path);
278
+        if (is_resource($dh)) {
279
+            while (($file = readdir($dh)) !== false) {
280
+                if (!\OC\Files\Filesystem::isIgnoredDir($file)) {
281
+                    if ($this->is_dir($path . '/' . $file)) {
282
+                        mkdir($target . '/' . $file);
283
+                        $this->addLocalFolder($path . '/' . $file, $target . '/' . $file);
284
+                    } else {
285
+                        $tmp = $this->toTmpFile($path . '/' . $file);
286
+                        rename($tmp, $target . '/' . $file);
287
+                    }
288
+                }
289
+            }
290
+        }
291
+    }
292
+
293
+    /**
294
+     * @param string $query
295
+     * @param string $dir
296
+     * @return array
297
+     */
298
+    protected function searchInDir($query, $dir = '') {
299
+        $files = [];
300
+        $dh = $this->opendir($dir);
301
+        if (is_resource($dh)) {
302
+            while (($item = readdir($dh)) !== false) {
303
+                if (\OC\Files\Filesystem::isIgnoredDir($item)) {
304
+                    continue;
305
+                }
306
+                if (strstr(strtolower($item), strtolower($query)) !== false) {
307
+                    $files[] = $dir . '/' . $item;
308
+                }
309
+                if ($this->is_dir($dir . '/' . $item)) {
310
+                    $files = array_merge($files, $this->searchInDir($query, $dir . '/' . $item));
311
+                }
312
+            }
313
+        }
314
+        closedir($dh);
315
+        return $files;
316
+    }
317
+
318
+    /**
319
+     * check if a file or folder has been updated since $time
320
+     *
321
+     * The method is only used to check if the cache needs to be updated. Storage backends that don't support checking
322
+     * the mtime should always return false here. As a result storage implementations that always return false expect
323
+     * exclusive access to the backend and will not pick up files that have been added in a way that circumvents
324
+     * ownClouds filesystem.
325
+     *
326
+     * @param string $path
327
+     * @param int $time
328
+     * @return bool
329
+     */
330
+    public function hasUpdated($path, $time) {
331
+        return $this->filemtime($path) > $time;
332
+    }
333
+
334
+    public function getCache($path = '', $storage = null) {
335
+        if (!$storage) {
336
+            $storage = $this;
337
+        }
338
+        if (!isset($storage->cache)) {
339
+            $storage->cache = new Cache($storage);
340
+        }
341
+        return $storage->cache;
342
+    }
343
+
344
+    public function getScanner($path = '', $storage = null) {
345
+        if (!$storage) {
346
+            $storage = $this;
347
+        }
348
+        if (!isset($storage->scanner)) {
349
+            $storage->scanner = new Scanner($storage);
350
+        }
351
+        return $storage->scanner;
352
+    }
353
+
354
+    public function getWatcher($path = '', $storage = null) {
355
+        if (!$storage) {
356
+            $storage = $this;
357
+        }
358
+        if (!isset($this->watcher)) {
359
+            $this->watcher = new Watcher($storage);
360
+            $globalPolicy = \OC::$server->getConfig()->getSystemValue('filesystem_check_changes', Watcher::CHECK_NEVER);
361
+            $this->watcher->setPolicy((int)$this->getMountOption('filesystem_check_changes', $globalPolicy));
362
+        }
363
+        return $this->watcher;
364
+    }
365
+
366
+    /**
367
+     * get a propagator instance for the cache
368
+     *
369
+     * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher
370
+     * @return \OC\Files\Cache\Propagator
371
+     */
372
+    public function getPropagator($storage = null) {
373
+        if (!$storage) {
374
+            $storage = $this;
375
+        }
376
+        if (!isset($storage->propagator)) {
377
+            $config = \OC::$server->getSystemConfig();
378
+            $storage->propagator = new Propagator($storage, \OC::$server->getDatabaseConnection(), ['appdata_' . $config->getValue('instanceid')]);
379
+        }
380
+        return $storage->propagator;
381
+    }
382
+
383
+    public function getUpdater($storage = null) {
384
+        if (!$storage) {
385
+            $storage = $this;
386
+        }
387
+        if (!isset($storage->updater)) {
388
+            $storage->updater = new Updater($storage);
389
+        }
390
+        return $storage->updater;
391
+    }
392
+
393
+    public function getStorageCache($storage = null) {
394
+        if (!$storage) {
395
+            $storage = $this;
396
+        }
397
+        if (!isset($this->storageCache)) {
398
+            $this->storageCache = new \OC\Files\Cache\Storage($storage);
399
+        }
400
+        return $this->storageCache;
401
+    }
402
+
403
+    /**
404
+     * get the owner of a path
405
+     *
406
+     * @param string $path The path to get the owner
407
+     * @return string|false uid or false
408
+     */
409
+    public function getOwner($path) {
410
+        if ($this->owner === null) {
411
+            $this->owner = \OC_User::getUser();
412
+        }
413
+
414
+        return $this->owner;
415
+    }
416
+
417
+    /**
418
+     * get the ETag for a file or folder
419
+     *
420
+     * @param string $path
421
+     * @return string
422
+     */
423
+    public function getETag($path) {
424
+        return uniqid();
425
+    }
426
+
427
+    /**
428
+     * clean a path, i.e. remove all redundant '.' and '..'
429
+     * making sure that it can't point to higher than '/'
430
+     *
431
+     * @param string $path The path to clean
432
+     * @return string cleaned path
433
+     */
434
+    public function cleanPath($path) {
435
+        if (strlen($path) == 0 or $path[0] != '/') {
436
+            $path = '/' . $path;
437
+        }
438
+
439
+        $output = [];
440
+        foreach (explode('/', $path) as $chunk) {
441
+            if ($chunk == '..') {
442
+                array_pop($output);
443
+            } elseif ($chunk == '.') {
444
+            } else {
445
+                $output[] = $chunk;
446
+            }
447
+        }
448
+        return implode('/', $output);
449
+    }
450
+
451
+    /**
452
+     * Test a storage for availability
453
+     *
454
+     * @return bool
455
+     */
456
+    public function test() {
457
+        try {
458
+            if ($this->stat('')) {
459
+                return true;
460
+            }
461
+            \OC::$server->getLogger()->info("External storage not available: stat() failed");
462
+            return false;
463
+        } catch (\Exception $e) {
464
+            \OC::$server->getLogger()->warning("External storage not available: " . $e->getMessage());
465
+            \OC::$server->getLogger()->logException($e, ['level' => ILogger::WARN]);
466
+            return false;
467
+        }
468
+    }
469
+
470
+    /**
471
+     * get the free space in the storage
472
+     *
473
+     * @param string $path
474
+     * @return int|false
475
+     */
476
+    public function free_space($path) {
477
+        return \OCP\Files\FileInfo::SPACE_UNKNOWN;
478
+    }
479
+
480
+    /**
481
+     * {@inheritdoc}
482
+     */
483
+    public function isLocal() {
484
+        // the common implementation returns a temporary file by
485
+        // default, which is not local
486
+        return false;
487
+    }
488
+
489
+    /**
490
+     * Check if the storage is an instance of $class or is a wrapper for a storage that is an instance of $class
491
+     *
492
+     * @param string $class
493
+     * @return bool
494
+     */
495
+    public function instanceOfStorage($class) {
496
+        if (ltrim($class, '\\') === 'OC\Files\Storage\Shared') {
497
+            // FIXME Temporary fix to keep existing checks working
498
+            $class = '\OCA\Files_Sharing\SharedStorage';
499
+        }
500
+        return is_a($this, $class);
501
+    }
502
+
503
+    /**
504
+     * A custom storage implementation can return an url for direct download of a give file.
505
+     *
506
+     * For now the returned array can hold the parameter url - in future more attributes might follow.
507
+     *
508
+     * @param string $path
509
+     * @return array|false
510
+     */
511
+    public function getDirectDownload($path) {
512
+        return [];
513
+    }
514
+
515
+    /**
516
+     * @inheritdoc
517
+     * @throws InvalidPathException
518
+     */
519
+    public function verifyPath($path, $fileName) {
520
+
521
+        // verify empty and dot files
522
+        $trimmed = trim($fileName);
523
+        if ($trimmed === '') {
524
+            throw new EmptyFileNameException();
525
+        }
526
+
527
+        if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) {
528
+            throw new InvalidDirectoryException();
529
+        }
530
+
531
+        if (!\OC::$server->getDatabaseConnection()->supports4ByteText()) {
532
+            // verify database - e.g. mysql only 3-byte chars
533
+            if (preg_match('%(?:
534 534
       \xF0[\x90-\xBF][\x80-\xBF]{2}      # planes 1-3
535 535
     | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
536 536
     | \xF4[\x80-\x8F][\x80-\xBF]{2}      # plane 16
537 537
 )%xs', $fileName)) {
538
-				throw new InvalidCharacterInPathException();
539
-			}
540
-		}
541
-
542
-		// 255 characters is the limit on common file systems (ext/xfs)
543
-		// oc_filecache has a 250 char length limit for the filename
544
-		if (isset($fileName[250])) {
545
-			throw new FileNameTooLongException();
546
-		}
547
-
548
-		// NOTE: $path will remain unverified for now
549
-		$this->verifyPosixPath($fileName);
550
-	}
551
-
552
-	/**
553
-	 * @param string $fileName
554
-	 * @throws InvalidPathException
555
-	 */
556
-	protected function verifyPosixPath($fileName) {
557
-		$fileName = trim($fileName);
558
-		$this->scanForInvalidCharacters($fileName, "\\/");
559
-		$reservedNames = ['*'];
560
-		if (in_array($fileName, $reservedNames)) {
561
-			throw new ReservedWordException();
562
-		}
563
-	}
564
-
565
-	/**
566
-	 * @param string $fileName
567
-	 * @param string $invalidChars
568
-	 * @throws InvalidPathException
569
-	 */
570
-	private function scanForInvalidCharacters($fileName, $invalidChars) {
571
-		foreach (str_split($invalidChars) as $char) {
572
-			if (strpos($fileName, $char) !== false) {
573
-				throw new InvalidCharacterInPathException();
574
-			}
575
-		}
576
-
577
-		$sanitizedFileName = filter_var($fileName, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);
578
-		if ($sanitizedFileName !== $fileName) {
579
-			throw new InvalidCharacterInPathException();
580
-		}
581
-	}
582
-
583
-	/**
584
-	 * @param array $options
585
-	 */
586
-	public function setMountOptions(array $options) {
587
-		$this->mountOptions = $options;
588
-	}
589
-
590
-	/**
591
-	 * @param string $name
592
-	 * @param mixed $default
593
-	 * @return mixed
594
-	 */
595
-	public function getMountOption($name, $default = null) {
596
-		return isset($this->mountOptions[$name]) ? $this->mountOptions[$name] : $default;
597
-	}
598
-
599
-	/**
600
-	 * @param IStorage $sourceStorage
601
-	 * @param string $sourceInternalPath
602
-	 * @param string $targetInternalPath
603
-	 * @param bool $preserveMtime
604
-	 * @return bool
605
-	 */
606
-	public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
607
-		if ($sourceStorage === $this) {
608
-			return $this->copy($sourceInternalPath, $targetInternalPath);
609
-		}
610
-
611
-		if ($sourceStorage->is_dir($sourceInternalPath)) {
612
-			$dh = $sourceStorage->opendir($sourceInternalPath);
613
-			$result = $this->mkdir($targetInternalPath);
614
-			if (is_resource($dh)) {
615
-				while ($result and ($file = readdir($dh)) !== false) {
616
-					if (!Filesystem::isIgnoredDir($file)) {
617
-						$result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file);
618
-					}
619
-				}
620
-			}
621
-		} else {
622
-			$source = $sourceStorage->fopen($sourceInternalPath, 'r');
623
-			$result = false;
624
-			if ($source) {
625
-				try {
626
-					$this->writeStream($targetInternalPath, $source);
627
-					$result = true;
628
-				} catch (\Exception $e) {
629
-					\OC::$server->getLogger()->logException($e, ['level' => ILogger::WARN, 'message' => 'Failed to copy stream to storage']);
630
-				}
631
-			}
632
-
633
-			if ($result && $preserveMtime) {
634
-				$mtime = $sourceStorage->filemtime($sourceInternalPath);
635
-				$this->touch($targetInternalPath, is_int($mtime) ? $mtime : null);
636
-			}
637
-
638
-			if (!$result) {
639
-				// delete partially written target file
640
-				$this->unlink($targetInternalPath);
641
-				// delete cache entry that was created by fopen
642
-				$this->getCache()->remove($targetInternalPath);
643
-			}
644
-		}
645
-		return (bool)$result;
646
-	}
647
-
648
-	/**
649
-	 * Check if a storage is the same as the current one, including wrapped storages
650
-	 *
651
-	 * @param IStorage $storage
652
-	 * @return bool
653
-	 */
654
-	private function isSameStorage(IStorage $storage): bool {
655
-		while ($storage->instanceOfStorage(Wrapper::class)) {
656
-			/**
657
-			 * @var Wrapper $sourceStorage
658
-			 */
659
-			$storage = $storage->getWrapperStorage();
660
-		}
661
-
662
-		return $storage === $this;
663
-	}
664
-
665
-	/**
666
-	 * @param IStorage $sourceStorage
667
-	 * @param string $sourceInternalPath
668
-	 * @param string $targetInternalPath
669
-	 * @return bool
670
-	 */
671
-	public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
672
-		if ($this->isSameStorage($sourceStorage)) {
673
-			// resolve any jailed paths
674
-			while ($sourceStorage->instanceOfStorage(Jail::class)) {
675
-				/**
676
-				 * @var Jail $sourceStorage
677
-				 */
678
-				$sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
679
-				$sourceStorage = $sourceStorage->getUnjailedStorage();
680
-			}
681
-
682
-			return $this->rename($sourceInternalPath, $targetInternalPath);
683
-		}
684
-
685
-		if (!$sourceStorage->isDeletable($sourceInternalPath)) {
686
-			return false;
687
-		}
688
-
689
-		$result = $this->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, true);
690
-		if ($result) {
691
-			if ($sourceStorage->is_dir($sourceInternalPath)) {
692
-				$result = $result && $sourceStorage->rmdir($sourceInternalPath);
693
-			} else {
694
-				$result = $result && $sourceStorage->unlink($sourceInternalPath);
695
-			}
696
-		}
697
-		return $result;
698
-	}
699
-
700
-	/**
701
-	 * @inheritdoc
702
-	 */
703
-	public function getMetaData($path) {
704
-		$permissions = $this->getPermissions($path);
705
-		if (!$permissions & \OCP\Constants::PERMISSION_READ) {
706
-			//can't read, nothing we can do
707
-			return null;
708
-		}
709
-
710
-		$data = [];
711
-		$data['mimetype'] = $this->getMimeType($path);
712
-		$data['mtime'] = $this->filemtime($path);
713
-		if ($data['mtime'] === false) {
714
-			$data['mtime'] = time();
715
-		}
716
-		if ($data['mimetype'] == 'httpd/unix-directory') {
717
-			$data['size'] = -1; //unknown
718
-		} else {
719
-			$data['size'] = $this->filesize($path);
720
-		}
721
-		$data['etag'] = $this->getETag($path);
722
-		$data['storage_mtime'] = $data['mtime'];
723
-		$data['permissions'] = $permissions;
724
-		$data['name'] = basename($path);
725
-
726
-		return $data;
727
-	}
728
-
729
-	/**
730
-	 * @param string $path
731
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
732
-	 * @param \OCP\Lock\ILockingProvider $provider
733
-	 * @throws \OCP\Lock\LockedException
734
-	 */
735
-	public function acquireLock($path, $type, ILockingProvider $provider) {
736
-		$logger = $this->getLockLogger();
737
-		if ($logger) {
738
-			$typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
739
-			$logger->info(
740
-				sprintf(
741
-					'acquire %s lock on "%s" on storage "%s"',
742
-					$typeString,
743
-					$path,
744
-					$this->getId()
745
-				),
746
-				[
747
-					'app' => 'locking',
748
-				]
749
-			);
750
-		}
751
-		try {
752
-			$provider->acquireLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type, $this->getId() . '::' . $path);
753
-		} catch (LockedException $e) {
754
-			if ($logger) {
755
-				$logger->logException($e, ['level' => ILogger::INFO]);
756
-			}
757
-			throw $e;
758
-		}
759
-	}
760
-
761
-	/**
762
-	 * @param string $path
763
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
764
-	 * @param \OCP\Lock\ILockingProvider $provider
765
-	 * @throws \OCP\Lock\LockedException
766
-	 */
767
-	public function releaseLock($path, $type, ILockingProvider $provider) {
768
-		$logger = $this->getLockLogger();
769
-		if ($logger) {
770
-			$typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
771
-			$logger->info(
772
-				sprintf(
773
-					'release %s lock on "%s" on storage "%s"',
774
-					$typeString,
775
-					$path,
776
-					$this->getId()
777
-				),
778
-				[
779
-					'app' => 'locking',
780
-				]
781
-			);
782
-		}
783
-		try {
784
-			$provider->releaseLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
785
-		} catch (LockedException $e) {
786
-			if ($logger) {
787
-				$logger->logException($e, ['level' => ILogger::INFO]);
788
-			}
789
-			throw $e;
790
-		}
791
-	}
792
-
793
-	/**
794
-	 * @param string $path
795
-	 * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
796
-	 * @param \OCP\Lock\ILockingProvider $provider
797
-	 * @throws \OCP\Lock\LockedException
798
-	 */
799
-	public function changeLock($path, $type, ILockingProvider $provider) {
800
-		$logger = $this->getLockLogger();
801
-		if ($logger) {
802
-			$typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
803
-			$logger->info(
804
-				sprintf(
805
-					'change lock on "%s" to %s on storage "%s"',
806
-					$path,
807
-					$typeString,
808
-					$this->getId()
809
-				),
810
-				[
811
-					'app' => 'locking',
812
-				]
813
-			);
814
-		}
815
-		try {
816
-			$provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
817
-		} catch (LockedException $e) {
818
-			\OC::$server->getLogger()->logException($e, ['level' => ILogger::INFO]);
819
-			throw $e;
820
-		}
821
-	}
822
-
823
-	private function getLockLogger() {
824
-		if (is_null($this->shouldLogLocks)) {
825
-			$this->shouldLogLocks = \OC::$server->getConfig()->getSystemValue('filelocking.debug', false);
826
-			$this->logger = $this->shouldLogLocks ? \OC::$server->getLogger() : null;
827
-		}
828
-		return $this->logger;
829
-	}
830
-
831
-	/**
832
-	 * @return array [ available, last_checked ]
833
-	 */
834
-	public function getAvailability() {
835
-		return $this->getStorageCache()->getAvailability();
836
-	}
837
-
838
-	/**
839
-	 * @param bool $isAvailable
840
-	 */
841
-	public function setAvailability($isAvailable) {
842
-		$this->getStorageCache()->setAvailability($isAvailable);
843
-	}
844
-
845
-	/**
846
-	 * @return bool
847
-	 */
848
-	public function needsPartFile() {
849
-		return true;
850
-	}
851
-
852
-	/**
853
-	 * fallback implementation
854
-	 *
855
-	 * @param string $path
856
-	 * @param resource $stream
857
-	 * @param int $size
858
-	 * @return int
859
-	 */
860
-	public function writeStream(string $path, $stream, int $size = null): int {
861
-		$target = $this->fopen($path, 'w');
862
-		if (!$target) {
863
-			throw new GenericFileException("Failed to open $path for writing");
864
-		}
865
-		try {
866
-			[$count, $result] = \OC_Helper::streamCopy($stream, $target);
867
-			if (!$result) {
868
-				throw new GenericFileException("Failed to copy stream");
869
-			}
870
-		} finally {
871
-			fclose($target);
872
-			fclose($stream);
873
-		}
874
-		return $count;
875
-	}
876
-
877
-	public function getDirectoryContent($directory): \Traversable {
878
-		$dh = $this->opendir($directory);
879
-		if (is_resource($dh)) {
880
-			$basePath = rtrim($directory, '/');
881
-			while (($file = readdir($dh)) !== false) {
882
-				if (!Filesystem::isIgnoredDir($file) && !Filesystem::isFileBlacklisted($file)) {
883
-					$childPath = $basePath . '/' . trim($file, '/');
884
-					$metadata = $this->getMetaData($childPath);
885
-					if ($metadata !== null) {
886
-						yield $metadata;
887
-					}
888
-				}
889
-			}
890
-		}
891
-	}
538
+                throw new InvalidCharacterInPathException();
539
+            }
540
+        }
541
+
542
+        // 255 characters is the limit on common file systems (ext/xfs)
543
+        // oc_filecache has a 250 char length limit for the filename
544
+        if (isset($fileName[250])) {
545
+            throw new FileNameTooLongException();
546
+        }
547
+
548
+        // NOTE: $path will remain unverified for now
549
+        $this->verifyPosixPath($fileName);
550
+    }
551
+
552
+    /**
553
+     * @param string $fileName
554
+     * @throws InvalidPathException
555
+     */
556
+    protected function verifyPosixPath($fileName) {
557
+        $fileName = trim($fileName);
558
+        $this->scanForInvalidCharacters($fileName, "\\/");
559
+        $reservedNames = ['*'];
560
+        if (in_array($fileName, $reservedNames)) {
561
+            throw new ReservedWordException();
562
+        }
563
+    }
564
+
565
+    /**
566
+     * @param string $fileName
567
+     * @param string $invalidChars
568
+     * @throws InvalidPathException
569
+     */
570
+    private function scanForInvalidCharacters($fileName, $invalidChars) {
571
+        foreach (str_split($invalidChars) as $char) {
572
+            if (strpos($fileName, $char) !== false) {
573
+                throw new InvalidCharacterInPathException();
574
+            }
575
+        }
576
+
577
+        $sanitizedFileName = filter_var($fileName, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);
578
+        if ($sanitizedFileName !== $fileName) {
579
+            throw new InvalidCharacterInPathException();
580
+        }
581
+    }
582
+
583
+    /**
584
+     * @param array $options
585
+     */
586
+    public function setMountOptions(array $options) {
587
+        $this->mountOptions = $options;
588
+    }
589
+
590
+    /**
591
+     * @param string $name
592
+     * @param mixed $default
593
+     * @return mixed
594
+     */
595
+    public function getMountOption($name, $default = null) {
596
+        return isset($this->mountOptions[$name]) ? $this->mountOptions[$name] : $default;
597
+    }
598
+
599
+    /**
600
+     * @param IStorage $sourceStorage
601
+     * @param string $sourceInternalPath
602
+     * @param string $targetInternalPath
603
+     * @param bool $preserveMtime
604
+     * @return bool
605
+     */
606
+    public function copyFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime = false) {
607
+        if ($sourceStorage === $this) {
608
+            return $this->copy($sourceInternalPath, $targetInternalPath);
609
+        }
610
+
611
+        if ($sourceStorage->is_dir($sourceInternalPath)) {
612
+            $dh = $sourceStorage->opendir($sourceInternalPath);
613
+            $result = $this->mkdir($targetInternalPath);
614
+            if (is_resource($dh)) {
615
+                while ($result and ($file = readdir($dh)) !== false) {
616
+                    if (!Filesystem::isIgnoredDir($file)) {
617
+                        $result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file);
618
+                    }
619
+                }
620
+            }
621
+        } else {
622
+            $source = $sourceStorage->fopen($sourceInternalPath, 'r');
623
+            $result = false;
624
+            if ($source) {
625
+                try {
626
+                    $this->writeStream($targetInternalPath, $source);
627
+                    $result = true;
628
+                } catch (\Exception $e) {
629
+                    \OC::$server->getLogger()->logException($e, ['level' => ILogger::WARN, 'message' => 'Failed to copy stream to storage']);
630
+                }
631
+            }
632
+
633
+            if ($result && $preserveMtime) {
634
+                $mtime = $sourceStorage->filemtime($sourceInternalPath);
635
+                $this->touch($targetInternalPath, is_int($mtime) ? $mtime : null);
636
+            }
637
+
638
+            if (!$result) {
639
+                // delete partially written target file
640
+                $this->unlink($targetInternalPath);
641
+                // delete cache entry that was created by fopen
642
+                $this->getCache()->remove($targetInternalPath);
643
+            }
644
+        }
645
+        return (bool)$result;
646
+    }
647
+
648
+    /**
649
+     * Check if a storage is the same as the current one, including wrapped storages
650
+     *
651
+     * @param IStorage $storage
652
+     * @return bool
653
+     */
654
+    private function isSameStorage(IStorage $storage): bool {
655
+        while ($storage->instanceOfStorage(Wrapper::class)) {
656
+            /**
657
+             * @var Wrapper $sourceStorage
658
+             */
659
+            $storage = $storage->getWrapperStorage();
660
+        }
661
+
662
+        return $storage === $this;
663
+    }
664
+
665
+    /**
666
+     * @param IStorage $sourceStorage
667
+     * @param string $sourceInternalPath
668
+     * @param string $targetInternalPath
669
+     * @return bool
670
+     */
671
+    public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
672
+        if ($this->isSameStorage($sourceStorage)) {
673
+            // resolve any jailed paths
674
+            while ($sourceStorage->instanceOfStorage(Jail::class)) {
675
+                /**
676
+                 * @var Jail $sourceStorage
677
+                 */
678
+                $sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath);
679
+                $sourceStorage = $sourceStorage->getUnjailedStorage();
680
+            }
681
+
682
+            return $this->rename($sourceInternalPath, $targetInternalPath);
683
+        }
684
+
685
+        if (!$sourceStorage->isDeletable($sourceInternalPath)) {
686
+            return false;
687
+        }
688
+
689
+        $result = $this->copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, true);
690
+        if ($result) {
691
+            if ($sourceStorage->is_dir($sourceInternalPath)) {
692
+                $result = $result && $sourceStorage->rmdir($sourceInternalPath);
693
+            } else {
694
+                $result = $result && $sourceStorage->unlink($sourceInternalPath);
695
+            }
696
+        }
697
+        return $result;
698
+    }
699
+
700
+    /**
701
+     * @inheritdoc
702
+     */
703
+    public function getMetaData($path) {
704
+        $permissions = $this->getPermissions($path);
705
+        if (!$permissions & \OCP\Constants::PERMISSION_READ) {
706
+            //can't read, nothing we can do
707
+            return null;
708
+        }
709
+
710
+        $data = [];
711
+        $data['mimetype'] = $this->getMimeType($path);
712
+        $data['mtime'] = $this->filemtime($path);
713
+        if ($data['mtime'] === false) {
714
+            $data['mtime'] = time();
715
+        }
716
+        if ($data['mimetype'] == 'httpd/unix-directory') {
717
+            $data['size'] = -1; //unknown
718
+        } else {
719
+            $data['size'] = $this->filesize($path);
720
+        }
721
+        $data['etag'] = $this->getETag($path);
722
+        $data['storage_mtime'] = $data['mtime'];
723
+        $data['permissions'] = $permissions;
724
+        $data['name'] = basename($path);
725
+
726
+        return $data;
727
+    }
728
+
729
+    /**
730
+     * @param string $path
731
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
732
+     * @param \OCP\Lock\ILockingProvider $provider
733
+     * @throws \OCP\Lock\LockedException
734
+     */
735
+    public function acquireLock($path, $type, ILockingProvider $provider) {
736
+        $logger = $this->getLockLogger();
737
+        if ($logger) {
738
+            $typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
739
+            $logger->info(
740
+                sprintf(
741
+                    'acquire %s lock on "%s" on storage "%s"',
742
+                    $typeString,
743
+                    $path,
744
+                    $this->getId()
745
+                ),
746
+                [
747
+                    'app' => 'locking',
748
+                ]
749
+            );
750
+        }
751
+        try {
752
+            $provider->acquireLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type, $this->getId() . '::' . $path);
753
+        } catch (LockedException $e) {
754
+            if ($logger) {
755
+                $logger->logException($e, ['level' => ILogger::INFO]);
756
+            }
757
+            throw $e;
758
+        }
759
+    }
760
+
761
+    /**
762
+     * @param string $path
763
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
764
+     * @param \OCP\Lock\ILockingProvider $provider
765
+     * @throws \OCP\Lock\LockedException
766
+     */
767
+    public function releaseLock($path, $type, ILockingProvider $provider) {
768
+        $logger = $this->getLockLogger();
769
+        if ($logger) {
770
+            $typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
771
+            $logger->info(
772
+                sprintf(
773
+                    'release %s lock on "%s" on storage "%s"',
774
+                    $typeString,
775
+                    $path,
776
+                    $this->getId()
777
+                ),
778
+                [
779
+                    'app' => 'locking',
780
+                ]
781
+            );
782
+        }
783
+        try {
784
+            $provider->releaseLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
785
+        } catch (LockedException $e) {
786
+            if ($logger) {
787
+                $logger->logException($e, ['level' => ILogger::INFO]);
788
+            }
789
+            throw $e;
790
+        }
791
+    }
792
+
793
+    /**
794
+     * @param string $path
795
+     * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
796
+     * @param \OCP\Lock\ILockingProvider $provider
797
+     * @throws \OCP\Lock\LockedException
798
+     */
799
+    public function changeLock($path, $type, ILockingProvider $provider) {
800
+        $logger = $this->getLockLogger();
801
+        if ($logger) {
802
+            $typeString = ($type === ILockingProvider::LOCK_SHARED) ? 'shared' : 'exclusive';
803
+            $logger->info(
804
+                sprintf(
805
+                    'change lock on "%s" to %s on storage "%s"',
806
+                    $path,
807
+                    $typeString,
808
+                    $this->getId()
809
+                ),
810
+                [
811
+                    'app' => 'locking',
812
+                ]
813
+            );
814
+        }
815
+        try {
816
+            $provider->changeLock('files/' . md5($this->getId() . '::' . trim($path, '/')), $type);
817
+        } catch (LockedException $e) {
818
+            \OC::$server->getLogger()->logException($e, ['level' => ILogger::INFO]);
819
+            throw $e;
820
+        }
821
+    }
822
+
823
+    private function getLockLogger() {
824
+        if (is_null($this->shouldLogLocks)) {
825
+            $this->shouldLogLocks = \OC::$server->getConfig()->getSystemValue('filelocking.debug', false);
826
+            $this->logger = $this->shouldLogLocks ? \OC::$server->getLogger() : null;
827
+        }
828
+        return $this->logger;
829
+    }
830
+
831
+    /**
832
+     * @return array [ available, last_checked ]
833
+     */
834
+    public function getAvailability() {
835
+        return $this->getStorageCache()->getAvailability();
836
+    }
837
+
838
+    /**
839
+     * @param bool $isAvailable
840
+     */
841
+    public function setAvailability($isAvailable) {
842
+        $this->getStorageCache()->setAvailability($isAvailable);
843
+    }
844
+
845
+    /**
846
+     * @return bool
847
+     */
848
+    public function needsPartFile() {
849
+        return true;
850
+    }
851
+
852
+    /**
853
+     * fallback implementation
854
+     *
855
+     * @param string $path
856
+     * @param resource $stream
857
+     * @param int $size
858
+     * @return int
859
+     */
860
+    public function writeStream(string $path, $stream, int $size = null): int {
861
+        $target = $this->fopen($path, 'w');
862
+        if (!$target) {
863
+            throw new GenericFileException("Failed to open $path for writing");
864
+        }
865
+        try {
866
+            [$count, $result] = \OC_Helper::streamCopy($stream, $target);
867
+            if (!$result) {
868
+                throw new GenericFileException("Failed to copy stream");
869
+            }
870
+        } finally {
871
+            fclose($target);
872
+            fclose($stream);
873
+        }
874
+        return $count;
875
+    }
876
+
877
+    public function getDirectoryContent($directory): \Traversable {
878
+        $dh = $this->opendir($directory);
879
+        if (is_resource($dh)) {
880
+            $basePath = rtrim($directory, '/');
881
+            while (($file = readdir($dh)) !== false) {
882
+                if (!Filesystem::isIgnoredDir($file) && !Filesystem::isFileBlacklisted($file)) {
883
+                    $childPath = $basePath . '/' . trim($file, '/');
884
+                    $metadata = $this->getMetaData($childPath);
885
+                    if ($metadata !== null) {
886
+                        yield $metadata;
887
+                    }
888
+                }
889
+            }
890
+        }
891
+    }
892 892
 }
Please login to merge, or discard this patch.