Completed
Pull Request — master (#9293)
by Blizzz
18:49
created
apps/user_ldap/lib/User/User.php 1 patch
Indentation   +645 added lines, -645 removed lines patch added patch discarded remove patch
@@ -47,655 +47,655 @@
 block discarded – undo
47 47
  * represents an LDAP user, gets and holds user-specific information from LDAP
48 48
  */
49 49
 class User {
50
-	/**
51
-	 * @var IUserTools
52
-	 */
53
-	protected $access;
54
-	/**
55
-	 * @var Connection
56
-	 */
57
-	protected $connection;
58
-	/**
59
-	 * @var IConfig
60
-	 */
61
-	protected $config;
62
-	/**
63
-	 * @var FilesystemHelper
64
-	 */
65
-	protected $fs;
66
-	/**
67
-	 * @var Image
68
-	 */
69
-	protected $image;
70
-	/**
71
-	 * @var LogWrapper
72
-	 */
73
-	protected $log;
74
-	/**
75
-	 * @var IAvatarManager
76
-	 */
77
-	protected $avatarManager;
78
-	/**
79
-	 * @var IUserManager
80
-	 */
81
-	protected $userManager;
82
-	/**
83
-	 * @var INotificationManager
84
-	 */
85
-	protected $notificationManager;
86
-	/**
87
-	 * @var string
88
-	 */
89
-	protected $dn;
90
-	/**
91
-	 * @var string
92
-	 */
93
-	protected $uid;
94
-	/**
95
-	 * @var string[]
96
-	 */
97
-	protected $refreshedFeatures = array();
98
-	/**
99
-	 * @var string
100
-	 */
101
-	protected $avatarImage;
102
-
103
-	/**
104
-	 * DB config keys for user preferences
105
-	 */
106
-	const USER_PREFKEY_FIRSTLOGIN  = 'firstLoginAccomplished';
107
-	const USER_PREFKEY_LASTREFRESH = 'lastFeatureRefresh';
108
-
109
-	/**
110
-	 * @brief constructor, make sure the subclasses call this one!
111
-	 * @param string $username the internal username
112
-	 * @param string $dn the LDAP DN
113
-	 * @param IUserTools $access an instance that implements IUserTools for
114
-	 * LDAP interaction
115
-	 * @param IConfig $config
116
-	 * @param FilesystemHelper $fs
117
-	 * @param Image $image any empty instance
118
-	 * @param LogWrapper $log
119
-	 * @param IAvatarManager $avatarManager
120
-	 * @param IUserManager $userManager
121
-	 * @param INotificationManager $notificationManager
122
-	 */
123
-	public function __construct($username, $dn, IUserTools $access,
124
-		IConfig $config, FilesystemHelper $fs, Image $image,
125
-		LogWrapper $log, IAvatarManager $avatarManager, IUserManager $userManager,
126
-		INotificationManager $notificationManager) {
50
+    /**
51
+     * @var IUserTools
52
+     */
53
+    protected $access;
54
+    /**
55
+     * @var Connection
56
+     */
57
+    protected $connection;
58
+    /**
59
+     * @var IConfig
60
+     */
61
+    protected $config;
62
+    /**
63
+     * @var FilesystemHelper
64
+     */
65
+    protected $fs;
66
+    /**
67
+     * @var Image
68
+     */
69
+    protected $image;
70
+    /**
71
+     * @var LogWrapper
72
+     */
73
+    protected $log;
74
+    /**
75
+     * @var IAvatarManager
76
+     */
77
+    protected $avatarManager;
78
+    /**
79
+     * @var IUserManager
80
+     */
81
+    protected $userManager;
82
+    /**
83
+     * @var INotificationManager
84
+     */
85
+    protected $notificationManager;
86
+    /**
87
+     * @var string
88
+     */
89
+    protected $dn;
90
+    /**
91
+     * @var string
92
+     */
93
+    protected $uid;
94
+    /**
95
+     * @var string[]
96
+     */
97
+    protected $refreshedFeatures = array();
98
+    /**
99
+     * @var string
100
+     */
101
+    protected $avatarImage;
102
+
103
+    /**
104
+     * DB config keys for user preferences
105
+     */
106
+    const USER_PREFKEY_FIRSTLOGIN  = 'firstLoginAccomplished';
107
+    const USER_PREFKEY_LASTREFRESH = 'lastFeatureRefresh';
108
+
109
+    /**
110
+     * @brief constructor, make sure the subclasses call this one!
111
+     * @param string $username the internal username
112
+     * @param string $dn the LDAP DN
113
+     * @param IUserTools $access an instance that implements IUserTools for
114
+     * LDAP interaction
115
+     * @param IConfig $config
116
+     * @param FilesystemHelper $fs
117
+     * @param Image $image any empty instance
118
+     * @param LogWrapper $log
119
+     * @param IAvatarManager $avatarManager
120
+     * @param IUserManager $userManager
121
+     * @param INotificationManager $notificationManager
122
+     */
123
+    public function __construct($username, $dn, IUserTools $access,
124
+        IConfig $config, FilesystemHelper $fs, Image $image,
125
+        LogWrapper $log, IAvatarManager $avatarManager, IUserManager $userManager,
126
+        INotificationManager $notificationManager) {
127 127
 	
128
-		if ($username === null) {
129
-			$log->log("uid for '$dn' must not be null!", ILogger::ERROR);
130
-			throw new \InvalidArgumentException('uid must not be null!');
131
-		} else if ($username === '') {
132
-			$log->log("uid for '$dn' must not be an empty string", ILogger::ERROR);
133
-			throw new \InvalidArgumentException('uid must not be an empty string!');
134
-		}
135
-
136
-		$this->access              = $access;
137
-		$this->connection          = $access->getConnection();
138
-		$this->config              = $config;
139
-		$this->fs                  = $fs;
140
-		$this->dn                  = $dn;
141
-		$this->uid                 = $username;
142
-		$this->image               = $image;
143
-		$this->log                 = $log;
144
-		$this->avatarManager       = $avatarManager;
145
-		$this->userManager         = $userManager;
146
-		$this->notificationManager = $notificationManager;
147
-
148
-		\OCP\Util::connectHook('OC_User', 'post_login', $this, 'handlePasswordExpiry');
149
-	}
150
-
151
-	/**
152
-	 * @brief updates properties like email, quota or avatar provided by LDAP
153
-	 * @return null
154
-	 */
155
-	public function update() {
156
-		if(is_null($this->dn)) {
157
-			return null;
158
-		}
159
-
160
-		$hasLoggedIn = $this->config->getUserValue($this->uid, 'user_ldap',
161
-				self::USER_PREFKEY_FIRSTLOGIN, 0);
162
-
163
-		if($this->needsRefresh()) {
164
-			$this->updateEmail();
165
-			$this->updateQuota();
166
-			if($hasLoggedIn !== 0) {
167
-				//we do not need to try it, when the user has not been logged in
168
-				//before, because the file system will not be ready.
169
-				$this->updateAvatar();
170
-				//in order to get an avatar as soon as possible, mark the user
171
-				//as refreshed only when updating the avatar did happen
172
-				$this->markRefreshTime();
173
-			}
174
-		}
175
-	}
176
-
177
-	/**
178
-	 * processes results from LDAP for attributes as returned by getAttributesToRead()
179
-	 * @param array $ldapEntry the user entry as retrieved from LDAP
180
-	 */
181
-	public function processAttributes($ldapEntry) {
182
-		$this->markRefreshTime();
183
-		//Quota
184
-		$attr = strtolower($this->connection->ldapQuotaAttribute);
185
-		if(isset($ldapEntry[$attr])) {
186
-			$this->updateQuota($ldapEntry[$attr][0]);
187
-		} else {
188
-			if ($this->connection->ldapQuotaDefault !== '') {
189
-				$this->updateQuota();
190
-			}
191
-		}
192
-		unset($attr);
193
-
194
-		//displayName
195
-		$displayName = $displayName2 = '';
196
-		$attr = strtolower($this->connection->ldapUserDisplayName);
197
-		if(isset($ldapEntry[$attr])) {
198
-			$displayName = (string)$ldapEntry[$attr][0];
199
-		}
200
-		$attr = strtolower($this->connection->ldapUserDisplayName2);
201
-		if(isset($ldapEntry[$attr])) {
202
-			$displayName2 = (string)$ldapEntry[$attr][0];
203
-		}
204
-		if ($displayName !== '') {
205
-			$this->composeAndStoreDisplayName($displayName);
206
-			$this->access->cacheUserDisplayName(
207
-				$this->getUsername(),
208
-				$displayName,
209
-				$displayName2
210
-			);
211
-		}
212
-		unset($attr);
213
-
214
-		//Email
215
-		//email must be stored after displayname, because it would cause a user
216
-		//change event that will trigger fetching the display name again
217
-		$attr = strtolower($this->connection->ldapEmailAttribute);
218
-		if(isset($ldapEntry[$attr])) {
219
-			$this->updateEmail($ldapEntry[$attr][0]);
220
-		}
221
-		unset($attr);
222
-
223
-		// LDAP Username, needed for s2s sharing
224
-		if(isset($ldapEntry['uid'])) {
225
-			$this->storeLDAPUserName($ldapEntry['uid'][0]);
226
-		} else if(isset($ldapEntry['samaccountname'])) {
227
-			$this->storeLDAPUserName($ldapEntry['samaccountname'][0]);
228
-		}
229
-
230
-		//homePath
231
-		if(strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) {
232
-			$attr = strtolower(substr($this->connection->homeFolderNamingRule, strlen('attr:')));
233
-			if(isset($ldapEntry[$attr])) {
234
-				$this->access->cacheUserHome(
235
-					$this->getUsername(), $this->getHomePath($ldapEntry[$attr][0]));
236
-			}
237
-		}
238
-
239
-		//memberOf groups
240
-		$cacheKey = 'getMemberOf'.$this->getUsername();
241
-		$groups = false;
242
-		if(isset($ldapEntry['memberof'])) {
243
-			$groups = $ldapEntry['memberof'];
244
-		}
245
-		$this->connection->writeToCache($cacheKey, $groups);
246
-
247
-		//Avatar
248
-		$attrs = array('jpegphoto', 'thumbnailphoto');
249
-		foreach ($attrs as $attr)  {
250
-			if(isset($ldapEntry[$attr])) {
251
-				$this->avatarImage = $ldapEntry[$attr][0];
252
-				// the call to the method that saves the avatar in the file
253
-				// system must be postponed after the login. It is to ensure
254
-				// external mounts are mounted properly (e.g. with login
255
-				// credentials from the session).
256
-				\OCP\Util::connectHook('OC_User', 'post_login', $this, 'updateAvatarPostLogin');
257
-				break;
258
-			}
259
-		}
260
-	}
261
-
262
-	/**
263
-	 * @brief returns the LDAP DN of the user
264
-	 * @return string
265
-	 */
266
-	public function getDN() {
267
-		return $this->dn;
268
-	}
269
-
270
-	/**
271
-	 * @brief returns the Nextcloud internal username of the user
272
-	 * @return string
273
-	 */
274
-	public function getUsername() {
275
-		return $this->uid;
276
-	}
277
-
278
-	/**
279
-	 * returns the home directory of the user if specified by LDAP settings
280
-	 * @param string $valueFromLDAP
281
-	 * @return bool|string
282
-	 * @throws \Exception
283
-	 */
284
-	public function getHomePath($valueFromLDAP = null) {
285
-		$path = (string)$valueFromLDAP;
286
-		$attr = null;
287
-
288
-		if (is_null($valueFromLDAP)
289
-		   && strpos($this->access->connection->homeFolderNamingRule, 'attr:') === 0
290
-		   && $this->access->connection->homeFolderNamingRule !== 'attr:')
291
-		{
292
-			$attr = substr($this->access->connection->homeFolderNamingRule, strlen('attr:'));
293
-			$homedir = $this->access->readAttribute(
294
-				$this->access->username2dn($this->getUsername()), $attr);
295
-			if ($homedir && isset($homedir[0])) {
296
-				$path = $homedir[0];
297
-			}
298
-		}
299
-
300
-		if ($path !== '') {
301
-			//if attribute's value is an absolute path take this, otherwise append it to data dir
302
-			//check for / at the beginning or pattern c:\ resp. c:/
303
-			if(   '/' !== $path[0]
304
-			   && !(3 < strlen($path) && ctype_alpha($path[0])
305
-			       && $path[1] === ':' && ('\\' === $path[2] || '/' === $path[2]))
306
-			) {
307
-				$path = $this->config->getSystemValue('datadirectory',
308
-						\OC::$SERVERROOT.'/data' ) . '/' . $path;
309
-			}
310
-			//we need it to store it in the DB as well in case a user gets
311
-			//deleted so we can clean up afterwards
312
-			$this->config->setUserValue(
313
-				$this->getUsername(), 'user_ldap', 'homePath', $path
314
-			);
315
-			return $path;
316
-		}
317
-
318
-		if(    !is_null($attr)
319
-			&& $this->config->getAppValue('user_ldap', 'enforce_home_folder_naming_rule', true)
320
-		) {
321
-			// a naming rule attribute is defined, but it doesn't exist for that LDAP user
322
-			throw new \Exception('Home dir attribute can\'t be read from LDAP for uid: ' . $this->getUsername());
323
-		}
324
-
325
-		//false will apply default behaviour as defined and done by OC_User
326
-		$this->config->setUserValue($this->getUsername(), 'user_ldap', 'homePath', '');
327
-		return false;
328
-	}
329
-
330
-	public function getMemberOfGroups() {
331
-		$cacheKey = 'getMemberOf'.$this->getUsername();
332
-		$memberOfGroups = $this->connection->getFromCache($cacheKey);
333
-		if(!is_null($memberOfGroups)) {
334
-			return $memberOfGroups;
335
-		}
336
-		$groupDNs = $this->access->readAttribute($this->getDN(), 'memberOf');
337
-		$this->connection->writeToCache($cacheKey, $groupDNs);
338
-		return $groupDNs;
339
-	}
340
-
341
-	/**
342
-	 * @brief reads the image from LDAP that shall be used as Avatar
343
-	 * @return string data (provided by LDAP) | false
344
-	 */
345
-	public function getAvatarImage() {
346
-		if(!is_null($this->avatarImage)) {
347
-			return $this->avatarImage;
348
-		}
349
-
350
-		$this->avatarImage = false;
351
-		$attributes = array('jpegPhoto', 'thumbnailPhoto');
352
-		foreach($attributes as $attribute) {
353
-			$result = $this->access->readAttribute($this->dn, $attribute);
354
-			if($result !== false && is_array($result) && isset($result[0])) {
355
-				$this->avatarImage = $result[0];
356
-				break;
357
-			}
358
-		}
359
-
360
-		return $this->avatarImage;
361
-	}
362
-
363
-	/**
364
-	 * @brief marks the user as having logged in at least once
365
-	 * @return null
366
-	 */
367
-	public function markLogin() {
368
-		$this->config->setUserValue(
369
-			$this->uid, 'user_ldap', self::USER_PREFKEY_FIRSTLOGIN, 1);
370
-	}
371
-
372
-	/**
373
-	 * @brief marks the time when user features like email have been updated
374
-	 * @return null
375
-	 */
376
-	public function markRefreshTime() {
377
-		$this->config->setUserValue(
378
-			$this->uid, 'user_ldap', self::USER_PREFKEY_LASTREFRESH, time());
379
-	}
380
-
381
-	/**
382
-	 * @brief checks whether user features needs to be updated again by
383
-	 * comparing the difference of time of the last refresh to now with the
384
-	 * desired interval
385
-	 * @return bool
386
-	 */
387
-	private function needsRefresh() {
388
-		$lastChecked = $this->config->getUserValue($this->uid, 'user_ldap',
389
-			self::USER_PREFKEY_LASTREFRESH, 0);
390
-
391
-		if((time() - (int)$lastChecked) < (int)$this->config->getAppValue('user_ldap', 'updateAttributesInterval', 86400)) {
392
-			return false;
393
-		}
394
-		return  true;
395
-	}
396
-
397
-	/**
398
-	 * Stores a key-value pair in relation to this user
399
-	 *
400
-	 * @param string $key
401
-	 * @param string $value
402
-	 */
403
-	private function store($key, $value) {
404
-		$this->config->setUserValue($this->uid, 'user_ldap', $key, $value);
405
-	}
406
-
407
-	/**
408
-	 * Composes the display name and stores it in the database. The final
409
-	 * display name is returned.
410
-	 *
411
-	 * @param string $displayName
412
-	 * @param string $displayName2
413
-	 * @returns string the effective display name
414
-	 */
415
-	public function composeAndStoreDisplayName($displayName, $displayName2 = '') {
416
-		$displayName2 = (string)$displayName2;
417
-		if($displayName2 !== '') {
418
-			$displayName .= ' (' . $displayName2 . ')';
419
-		}
420
-		$this->store('displayName', $displayName);
421
-		return $displayName;
422
-	}
423
-
424
-	/**
425
-	 * Stores the LDAP Username in the Database
426
-	 * @param string $userName
427
-	 */
428
-	public function storeLDAPUserName($userName) {
429
-		$this->store('uid', $userName);
430
-	}
431
-
432
-	/**
433
-	 * @brief checks whether an update method specified by feature was run
434
-	 * already. If not, it will marked like this, because it is expected that
435
-	 * the method will be run, when false is returned.
436
-	 * @param string $feature email | quota | avatar (can be extended)
437
-	 * @return bool
438
-	 */
439
-	private function wasRefreshed($feature) {
440
-		if(isset($this->refreshedFeatures[$feature])) {
441
-			return true;
442
-		}
443
-		$this->refreshedFeatures[$feature] = 1;
444
-		return false;
445
-	}
446
-
447
-	/**
448
-	 * fetches the email from LDAP and stores it as Nextcloud user value
449
-	 * @param string $valueFromLDAP if known, to save an LDAP read request
450
-	 * @return null
451
-	 */
452
-	public function updateEmail($valueFromLDAP = null) {
453
-		if($this->wasRefreshed('email')) {
454
-			return;
455
-		}
456
-		$email = (string)$valueFromLDAP;
457
-		if(is_null($valueFromLDAP)) {
458
-			$emailAttribute = $this->connection->ldapEmailAttribute;
459
-			if ($emailAttribute !== '') {
460
-				$aEmail = $this->access->readAttribute($this->dn, $emailAttribute);
461
-				if(is_array($aEmail) && (count($aEmail) > 0)) {
462
-					$email = (string)$aEmail[0];
463
-				}
464
-			}
465
-		}
466
-		if ($email !== '') {
467
-			$user = $this->userManager->get($this->uid);
468
-			if (!is_null($user)) {
469
-				$currentEmail = (string)$user->getEMailAddress();
470
-				if ($currentEmail !== $email) {
471
-					$user->setEMailAddress($email);
472
-				}
473
-			}
474
-		}
475
-	}
476
-
477
-	/**
478
-	 * Overall process goes as follow:
479
-	 * 1. fetch the quota from LDAP and check if it's parseable with the "verifyQuotaValue" function
480
-	 * 2. if the value can't be fetched, is empty or not parseable, use the default LDAP quota
481
-	 * 3. if the default LDAP quota can't be parsed, use the Nextcloud's default quota (use 'default')
482
-	 * 4. check if the target user exists and set the quota for the user.
483
-	 *
484
-	 * In order to improve performance and prevent an unwanted extra LDAP call, the $valueFromLDAP
485
-	 * parameter can be passed with the value of the attribute. This value will be considered as the
486
-	 * quota for the user coming from the LDAP server (step 1 of the process) It can be useful to
487
-	 * fetch all the user's attributes in one call and use the fetched values in this function.
488
-	 * The expected value for that parameter is a string describing the quota for the user. Valid
489
-	 * values are 'none' (unlimited), 'default' (the Nextcloud's default quota), '1234' (quota in
490
-	 * bytes), '1234 MB' (quota in MB - check the \OC_Helper::computerFileSize method for more info)
491
-	 *
492
-	 * fetches the quota from LDAP and stores it as Nextcloud user value
493
-	 * @param string $valueFromLDAP the quota attribute's value can be passed,
494
-	 * to save the readAttribute request
495
-	 * @return null
496
-	 */
497
-	public function updateQuota($valueFromLDAP = null) {
498
-		if($this->wasRefreshed('quota')) {
499
-			return;
500
-		}
501
-
502
-		$quota = false;
503
-		if(is_null($valueFromLDAP)) {
504
-			$quotaAttribute = $this->connection->ldapQuotaAttribute;
505
-			if ($quotaAttribute !== '') {
506
-				$aQuota = $this->access->readAttribute($this->dn, $quotaAttribute);
507
-				if($aQuota && (count($aQuota) > 0)) {
508
-					if ($this->verifyQuotaValue($aQuota[0])) {
509
-						$quota = $aQuota[0];
510
-					} else {
511
-						$this->log->log('not suitable LDAP quota found for user ' . $this->uid . ': [' . $aQuota[0] . ']', ILogger::WARN);
512
-					}
513
-				}
514
-			}
515
-		} else {
516
-			if ($this->verifyQuotaValue($valueFromLDAP)) {
517
-				$quota = $valueFromLDAP;
518
-			} else {
519
-				$this->log->log('not suitable LDAP quota found for user ' . $this->uid . ': [' . $valueFromLDAP . ']', ILogger::WARN);
520
-			}
521
-		}
522
-
523
-		if ($quota === false) {
524
-			// quota not found using the LDAP attribute (or not parseable). Try the default quota
525
-			$defaultQuota = $this->connection->ldapQuotaDefault;
526
-			if ($this->verifyQuotaValue($defaultQuota)) {
527
-				$quota = $defaultQuota;
528
-			}
529
-		}
530
-
531
-		$targetUser = $this->userManager->get($this->uid);
532
-		if ($targetUser) {
533
-			if($quota !== false) {
534
-				$targetUser->setQuota($quota);
535
-			} else {
536
-				$this->log->log('not suitable default quota found for user ' . $this->uid . ': [' . $defaultQuota . ']', ILogger::WARN);
537
-			}
538
-		} else {
539
-			$this->log->log('trying to set a quota for user ' . $this->uid . ' but the user is missing', ILogger::ERROR);
540
-		}
541
-	}
542
-
543
-	private function verifyQuotaValue($quotaValue) {
544
-		return $quotaValue === 'none' || $quotaValue === 'default' || \OC_Helper::computerFileSize($quotaValue) !== false;
545
-	}
546
-
547
-	/**
548
-	 * called by a post_login hook to save the avatar picture
549
-	 *
550
-	 * @param array $params
551
-	 */
552
-	public function updateAvatarPostLogin($params) {
553
-		if(isset($params['uid']) && $params['uid'] === $this->getUsername()) {
554
-			$this->updateAvatar();
555
-		}
556
-	}
557
-
558
-	/**
559
-	 * @brief attempts to get an image from LDAP and sets it as Nextcloud avatar
560
-	 * @return null
561
-	 */
562
-	public function updateAvatar() {
563
-		if($this->wasRefreshed('avatar')) {
564
-			return;
565
-		}
566
-		$avatarImage = $this->getAvatarImage();
567
-		if($avatarImage === false) {
568
-			//not set, nothing left to do;
569
-			return;
570
-		}
571
-		$this->image->loadFromBase64(base64_encode($avatarImage));
572
-		$this->setOwnCloudAvatar();
573
-	}
574
-
575
-	/**
576
-	 * @brief sets an image as Nextcloud avatar
577
-	 * @return null
578
-	 */
579
-	private function setOwnCloudAvatar() {
580
-		if(!$this->image->valid()) {
581
-			$this->log->log('jpegPhoto data invalid for '.$this->dn, ILogger::ERROR);
582
-			return;
583
-		}
584
-		//make sure it is a square and not bigger than 128x128
585
-		$size = min(array($this->image->width(), $this->image->height(), 128));
586
-		if(!$this->image->centerCrop($size)) {
587
-			$this->log->log('croping image for avatar failed for '.$this->dn, ILogger::ERROR);
588
-			return;
589
-		}
590
-
591
-		if(!$this->fs->isLoaded()) {
592
-			$this->fs->setup($this->uid);
593
-		}
594
-
595
-		try {
596
-			$avatar = $this->avatarManager->getAvatar($this->uid);
597
-			$avatar->set($this->image);
598
-		} catch (\Exception $e) {
599
-			\OC::$server->getLogger()->logException($e, [
600
-				'message' => 'Could not set avatar for ' . $this->dn,
601
-				'level' => ILogger::INFO,
602
-				'app' => 'user_ldap',
603
-			]);
604
-		}
605
-	}
606
-
607
-	/**
608
-	 * called by a post_login hook to handle password expiry
609
-	 *
610
-	 * @param array $params
611
-	 */
612
-	public function handlePasswordExpiry($params) {
613
-		$ppolicyDN = $this->connection->ldapDefaultPPolicyDN;
614
-		if (empty($ppolicyDN) || ((int)$this->connection->turnOnPasswordChange !== 1)) {
615
-			return;//password expiry handling disabled
616
-		}
617
-		$uid = $params['uid'];
618
-		if(isset($uid) && $uid === $this->getUsername()) {
619
-			//retrieve relevant user attributes
620
-			$result = $this->access->search('objectclass=*', $this->dn, ['pwdpolicysubentry', 'pwdgraceusetime', 'pwdreset', 'pwdchangedtime']);
128
+        if ($username === null) {
129
+            $log->log("uid for '$dn' must not be null!", ILogger::ERROR);
130
+            throw new \InvalidArgumentException('uid must not be null!');
131
+        } else if ($username === '') {
132
+            $log->log("uid for '$dn' must not be an empty string", ILogger::ERROR);
133
+            throw new \InvalidArgumentException('uid must not be an empty string!');
134
+        }
135
+
136
+        $this->access              = $access;
137
+        $this->connection          = $access->getConnection();
138
+        $this->config              = $config;
139
+        $this->fs                  = $fs;
140
+        $this->dn                  = $dn;
141
+        $this->uid                 = $username;
142
+        $this->image               = $image;
143
+        $this->log                 = $log;
144
+        $this->avatarManager       = $avatarManager;
145
+        $this->userManager         = $userManager;
146
+        $this->notificationManager = $notificationManager;
147
+
148
+        \OCP\Util::connectHook('OC_User', 'post_login', $this, 'handlePasswordExpiry');
149
+    }
150
+
151
+    /**
152
+     * @brief updates properties like email, quota or avatar provided by LDAP
153
+     * @return null
154
+     */
155
+    public function update() {
156
+        if(is_null($this->dn)) {
157
+            return null;
158
+        }
159
+
160
+        $hasLoggedIn = $this->config->getUserValue($this->uid, 'user_ldap',
161
+                self::USER_PREFKEY_FIRSTLOGIN, 0);
162
+
163
+        if($this->needsRefresh()) {
164
+            $this->updateEmail();
165
+            $this->updateQuota();
166
+            if($hasLoggedIn !== 0) {
167
+                //we do not need to try it, when the user has not been logged in
168
+                //before, because the file system will not be ready.
169
+                $this->updateAvatar();
170
+                //in order to get an avatar as soon as possible, mark the user
171
+                //as refreshed only when updating the avatar did happen
172
+                $this->markRefreshTime();
173
+            }
174
+        }
175
+    }
176
+
177
+    /**
178
+     * processes results from LDAP for attributes as returned by getAttributesToRead()
179
+     * @param array $ldapEntry the user entry as retrieved from LDAP
180
+     */
181
+    public function processAttributes($ldapEntry) {
182
+        $this->markRefreshTime();
183
+        //Quota
184
+        $attr = strtolower($this->connection->ldapQuotaAttribute);
185
+        if(isset($ldapEntry[$attr])) {
186
+            $this->updateQuota($ldapEntry[$attr][0]);
187
+        } else {
188
+            if ($this->connection->ldapQuotaDefault !== '') {
189
+                $this->updateQuota();
190
+            }
191
+        }
192
+        unset($attr);
193
+
194
+        //displayName
195
+        $displayName = $displayName2 = '';
196
+        $attr = strtolower($this->connection->ldapUserDisplayName);
197
+        if(isset($ldapEntry[$attr])) {
198
+            $displayName = (string)$ldapEntry[$attr][0];
199
+        }
200
+        $attr = strtolower($this->connection->ldapUserDisplayName2);
201
+        if(isset($ldapEntry[$attr])) {
202
+            $displayName2 = (string)$ldapEntry[$attr][0];
203
+        }
204
+        if ($displayName !== '') {
205
+            $this->composeAndStoreDisplayName($displayName);
206
+            $this->access->cacheUserDisplayName(
207
+                $this->getUsername(),
208
+                $displayName,
209
+                $displayName2
210
+            );
211
+        }
212
+        unset($attr);
213
+
214
+        //Email
215
+        //email must be stored after displayname, because it would cause a user
216
+        //change event that will trigger fetching the display name again
217
+        $attr = strtolower($this->connection->ldapEmailAttribute);
218
+        if(isset($ldapEntry[$attr])) {
219
+            $this->updateEmail($ldapEntry[$attr][0]);
220
+        }
221
+        unset($attr);
222
+
223
+        // LDAP Username, needed for s2s sharing
224
+        if(isset($ldapEntry['uid'])) {
225
+            $this->storeLDAPUserName($ldapEntry['uid'][0]);
226
+        } else if(isset($ldapEntry['samaccountname'])) {
227
+            $this->storeLDAPUserName($ldapEntry['samaccountname'][0]);
228
+        }
229
+
230
+        //homePath
231
+        if(strpos($this->connection->homeFolderNamingRule, 'attr:') === 0) {
232
+            $attr = strtolower(substr($this->connection->homeFolderNamingRule, strlen('attr:')));
233
+            if(isset($ldapEntry[$attr])) {
234
+                $this->access->cacheUserHome(
235
+                    $this->getUsername(), $this->getHomePath($ldapEntry[$attr][0]));
236
+            }
237
+        }
238
+
239
+        //memberOf groups
240
+        $cacheKey = 'getMemberOf'.$this->getUsername();
241
+        $groups = false;
242
+        if(isset($ldapEntry['memberof'])) {
243
+            $groups = $ldapEntry['memberof'];
244
+        }
245
+        $this->connection->writeToCache($cacheKey, $groups);
246
+
247
+        //Avatar
248
+        $attrs = array('jpegphoto', 'thumbnailphoto');
249
+        foreach ($attrs as $attr)  {
250
+            if(isset($ldapEntry[$attr])) {
251
+                $this->avatarImage = $ldapEntry[$attr][0];
252
+                // the call to the method that saves the avatar in the file
253
+                // system must be postponed after the login. It is to ensure
254
+                // external mounts are mounted properly (e.g. with login
255
+                // credentials from the session).
256
+                \OCP\Util::connectHook('OC_User', 'post_login', $this, 'updateAvatarPostLogin');
257
+                break;
258
+            }
259
+        }
260
+    }
261
+
262
+    /**
263
+     * @brief returns the LDAP DN of the user
264
+     * @return string
265
+     */
266
+    public function getDN() {
267
+        return $this->dn;
268
+    }
269
+
270
+    /**
271
+     * @brief returns the Nextcloud internal username of the user
272
+     * @return string
273
+     */
274
+    public function getUsername() {
275
+        return $this->uid;
276
+    }
277
+
278
+    /**
279
+     * returns the home directory of the user if specified by LDAP settings
280
+     * @param string $valueFromLDAP
281
+     * @return bool|string
282
+     * @throws \Exception
283
+     */
284
+    public function getHomePath($valueFromLDAP = null) {
285
+        $path = (string)$valueFromLDAP;
286
+        $attr = null;
287
+
288
+        if (is_null($valueFromLDAP)
289
+           && strpos($this->access->connection->homeFolderNamingRule, 'attr:') === 0
290
+           && $this->access->connection->homeFolderNamingRule !== 'attr:')
291
+        {
292
+            $attr = substr($this->access->connection->homeFolderNamingRule, strlen('attr:'));
293
+            $homedir = $this->access->readAttribute(
294
+                $this->access->username2dn($this->getUsername()), $attr);
295
+            if ($homedir && isset($homedir[0])) {
296
+                $path = $homedir[0];
297
+            }
298
+        }
299
+
300
+        if ($path !== '') {
301
+            //if attribute's value is an absolute path take this, otherwise append it to data dir
302
+            //check for / at the beginning or pattern c:\ resp. c:/
303
+            if(   '/' !== $path[0]
304
+               && !(3 < strlen($path) && ctype_alpha($path[0])
305
+                   && $path[1] === ':' && ('\\' === $path[2] || '/' === $path[2]))
306
+            ) {
307
+                $path = $this->config->getSystemValue('datadirectory',
308
+                        \OC::$SERVERROOT.'/data' ) . '/' . $path;
309
+            }
310
+            //we need it to store it in the DB as well in case a user gets
311
+            //deleted so we can clean up afterwards
312
+            $this->config->setUserValue(
313
+                $this->getUsername(), 'user_ldap', 'homePath', $path
314
+            );
315
+            return $path;
316
+        }
317
+
318
+        if(    !is_null($attr)
319
+            && $this->config->getAppValue('user_ldap', 'enforce_home_folder_naming_rule', true)
320
+        ) {
321
+            // a naming rule attribute is defined, but it doesn't exist for that LDAP user
322
+            throw new \Exception('Home dir attribute can\'t be read from LDAP for uid: ' . $this->getUsername());
323
+        }
324
+
325
+        //false will apply default behaviour as defined and done by OC_User
326
+        $this->config->setUserValue($this->getUsername(), 'user_ldap', 'homePath', '');
327
+        return false;
328
+    }
329
+
330
+    public function getMemberOfGroups() {
331
+        $cacheKey = 'getMemberOf'.$this->getUsername();
332
+        $memberOfGroups = $this->connection->getFromCache($cacheKey);
333
+        if(!is_null($memberOfGroups)) {
334
+            return $memberOfGroups;
335
+        }
336
+        $groupDNs = $this->access->readAttribute($this->getDN(), 'memberOf');
337
+        $this->connection->writeToCache($cacheKey, $groupDNs);
338
+        return $groupDNs;
339
+    }
340
+
341
+    /**
342
+     * @brief reads the image from LDAP that shall be used as Avatar
343
+     * @return string data (provided by LDAP) | false
344
+     */
345
+    public function getAvatarImage() {
346
+        if(!is_null($this->avatarImage)) {
347
+            return $this->avatarImage;
348
+        }
349
+
350
+        $this->avatarImage = false;
351
+        $attributes = array('jpegPhoto', 'thumbnailPhoto');
352
+        foreach($attributes as $attribute) {
353
+            $result = $this->access->readAttribute($this->dn, $attribute);
354
+            if($result !== false && is_array($result) && isset($result[0])) {
355
+                $this->avatarImage = $result[0];
356
+                break;
357
+            }
358
+        }
359
+
360
+        return $this->avatarImage;
361
+    }
362
+
363
+    /**
364
+     * @brief marks the user as having logged in at least once
365
+     * @return null
366
+     */
367
+    public function markLogin() {
368
+        $this->config->setUserValue(
369
+            $this->uid, 'user_ldap', self::USER_PREFKEY_FIRSTLOGIN, 1);
370
+    }
371
+
372
+    /**
373
+     * @brief marks the time when user features like email have been updated
374
+     * @return null
375
+     */
376
+    public function markRefreshTime() {
377
+        $this->config->setUserValue(
378
+            $this->uid, 'user_ldap', self::USER_PREFKEY_LASTREFRESH, time());
379
+    }
380
+
381
+    /**
382
+     * @brief checks whether user features needs to be updated again by
383
+     * comparing the difference of time of the last refresh to now with the
384
+     * desired interval
385
+     * @return bool
386
+     */
387
+    private function needsRefresh() {
388
+        $lastChecked = $this->config->getUserValue($this->uid, 'user_ldap',
389
+            self::USER_PREFKEY_LASTREFRESH, 0);
390
+
391
+        if((time() - (int)$lastChecked) < (int)$this->config->getAppValue('user_ldap', 'updateAttributesInterval', 86400)) {
392
+            return false;
393
+        }
394
+        return  true;
395
+    }
396
+
397
+    /**
398
+     * Stores a key-value pair in relation to this user
399
+     *
400
+     * @param string $key
401
+     * @param string $value
402
+     */
403
+    private function store($key, $value) {
404
+        $this->config->setUserValue($this->uid, 'user_ldap', $key, $value);
405
+    }
406
+
407
+    /**
408
+     * Composes the display name and stores it in the database. The final
409
+     * display name is returned.
410
+     *
411
+     * @param string $displayName
412
+     * @param string $displayName2
413
+     * @returns string the effective display name
414
+     */
415
+    public function composeAndStoreDisplayName($displayName, $displayName2 = '') {
416
+        $displayName2 = (string)$displayName2;
417
+        if($displayName2 !== '') {
418
+            $displayName .= ' (' . $displayName2 . ')';
419
+        }
420
+        $this->store('displayName', $displayName);
421
+        return $displayName;
422
+    }
423
+
424
+    /**
425
+     * Stores the LDAP Username in the Database
426
+     * @param string $userName
427
+     */
428
+    public function storeLDAPUserName($userName) {
429
+        $this->store('uid', $userName);
430
+    }
431
+
432
+    /**
433
+     * @brief checks whether an update method specified by feature was run
434
+     * already. If not, it will marked like this, because it is expected that
435
+     * the method will be run, when false is returned.
436
+     * @param string $feature email | quota | avatar (can be extended)
437
+     * @return bool
438
+     */
439
+    private function wasRefreshed($feature) {
440
+        if(isset($this->refreshedFeatures[$feature])) {
441
+            return true;
442
+        }
443
+        $this->refreshedFeatures[$feature] = 1;
444
+        return false;
445
+    }
446
+
447
+    /**
448
+     * fetches the email from LDAP and stores it as Nextcloud user value
449
+     * @param string $valueFromLDAP if known, to save an LDAP read request
450
+     * @return null
451
+     */
452
+    public function updateEmail($valueFromLDAP = null) {
453
+        if($this->wasRefreshed('email')) {
454
+            return;
455
+        }
456
+        $email = (string)$valueFromLDAP;
457
+        if(is_null($valueFromLDAP)) {
458
+            $emailAttribute = $this->connection->ldapEmailAttribute;
459
+            if ($emailAttribute !== '') {
460
+                $aEmail = $this->access->readAttribute($this->dn, $emailAttribute);
461
+                if(is_array($aEmail) && (count($aEmail) > 0)) {
462
+                    $email = (string)$aEmail[0];
463
+                }
464
+            }
465
+        }
466
+        if ($email !== '') {
467
+            $user = $this->userManager->get($this->uid);
468
+            if (!is_null($user)) {
469
+                $currentEmail = (string)$user->getEMailAddress();
470
+                if ($currentEmail !== $email) {
471
+                    $user->setEMailAddress($email);
472
+                }
473
+            }
474
+        }
475
+    }
476
+
477
+    /**
478
+     * Overall process goes as follow:
479
+     * 1. fetch the quota from LDAP and check if it's parseable with the "verifyQuotaValue" function
480
+     * 2. if the value can't be fetched, is empty or not parseable, use the default LDAP quota
481
+     * 3. if the default LDAP quota can't be parsed, use the Nextcloud's default quota (use 'default')
482
+     * 4. check if the target user exists and set the quota for the user.
483
+     *
484
+     * In order to improve performance and prevent an unwanted extra LDAP call, the $valueFromLDAP
485
+     * parameter can be passed with the value of the attribute. This value will be considered as the
486
+     * quota for the user coming from the LDAP server (step 1 of the process) It can be useful to
487
+     * fetch all the user's attributes in one call and use the fetched values in this function.
488
+     * The expected value for that parameter is a string describing the quota for the user. Valid
489
+     * values are 'none' (unlimited), 'default' (the Nextcloud's default quota), '1234' (quota in
490
+     * bytes), '1234 MB' (quota in MB - check the \OC_Helper::computerFileSize method for more info)
491
+     *
492
+     * fetches the quota from LDAP and stores it as Nextcloud user value
493
+     * @param string $valueFromLDAP the quota attribute's value can be passed,
494
+     * to save the readAttribute request
495
+     * @return null
496
+     */
497
+    public function updateQuota($valueFromLDAP = null) {
498
+        if($this->wasRefreshed('quota')) {
499
+            return;
500
+        }
501
+
502
+        $quota = false;
503
+        if(is_null($valueFromLDAP)) {
504
+            $quotaAttribute = $this->connection->ldapQuotaAttribute;
505
+            if ($quotaAttribute !== '') {
506
+                $aQuota = $this->access->readAttribute($this->dn, $quotaAttribute);
507
+                if($aQuota && (count($aQuota) > 0)) {
508
+                    if ($this->verifyQuotaValue($aQuota[0])) {
509
+                        $quota = $aQuota[0];
510
+                    } else {
511
+                        $this->log->log('not suitable LDAP quota found for user ' . $this->uid . ': [' . $aQuota[0] . ']', ILogger::WARN);
512
+                    }
513
+                }
514
+            }
515
+        } else {
516
+            if ($this->verifyQuotaValue($valueFromLDAP)) {
517
+                $quota = $valueFromLDAP;
518
+            } else {
519
+                $this->log->log('not suitable LDAP quota found for user ' . $this->uid . ': [' . $valueFromLDAP . ']', ILogger::WARN);
520
+            }
521
+        }
522
+
523
+        if ($quota === false) {
524
+            // quota not found using the LDAP attribute (or not parseable). Try the default quota
525
+            $defaultQuota = $this->connection->ldapQuotaDefault;
526
+            if ($this->verifyQuotaValue($defaultQuota)) {
527
+                $quota = $defaultQuota;
528
+            }
529
+        }
530
+
531
+        $targetUser = $this->userManager->get($this->uid);
532
+        if ($targetUser) {
533
+            if($quota !== false) {
534
+                $targetUser->setQuota($quota);
535
+            } else {
536
+                $this->log->log('not suitable default quota found for user ' . $this->uid . ': [' . $defaultQuota . ']', ILogger::WARN);
537
+            }
538
+        } else {
539
+            $this->log->log('trying to set a quota for user ' . $this->uid . ' but the user is missing', ILogger::ERROR);
540
+        }
541
+    }
542
+
543
+    private function verifyQuotaValue($quotaValue) {
544
+        return $quotaValue === 'none' || $quotaValue === 'default' || \OC_Helper::computerFileSize($quotaValue) !== false;
545
+    }
546
+
547
+    /**
548
+     * called by a post_login hook to save the avatar picture
549
+     *
550
+     * @param array $params
551
+     */
552
+    public function updateAvatarPostLogin($params) {
553
+        if(isset($params['uid']) && $params['uid'] === $this->getUsername()) {
554
+            $this->updateAvatar();
555
+        }
556
+    }
557
+
558
+    /**
559
+     * @brief attempts to get an image from LDAP and sets it as Nextcloud avatar
560
+     * @return null
561
+     */
562
+    public function updateAvatar() {
563
+        if($this->wasRefreshed('avatar')) {
564
+            return;
565
+        }
566
+        $avatarImage = $this->getAvatarImage();
567
+        if($avatarImage === false) {
568
+            //not set, nothing left to do;
569
+            return;
570
+        }
571
+        $this->image->loadFromBase64(base64_encode($avatarImage));
572
+        $this->setOwnCloudAvatar();
573
+    }
574
+
575
+    /**
576
+     * @brief sets an image as Nextcloud avatar
577
+     * @return null
578
+     */
579
+    private function setOwnCloudAvatar() {
580
+        if(!$this->image->valid()) {
581
+            $this->log->log('jpegPhoto data invalid for '.$this->dn, ILogger::ERROR);
582
+            return;
583
+        }
584
+        //make sure it is a square and not bigger than 128x128
585
+        $size = min(array($this->image->width(), $this->image->height(), 128));
586
+        if(!$this->image->centerCrop($size)) {
587
+            $this->log->log('croping image for avatar failed for '.$this->dn, ILogger::ERROR);
588
+            return;
589
+        }
590
+
591
+        if(!$this->fs->isLoaded()) {
592
+            $this->fs->setup($this->uid);
593
+        }
594
+
595
+        try {
596
+            $avatar = $this->avatarManager->getAvatar($this->uid);
597
+            $avatar->set($this->image);
598
+        } catch (\Exception $e) {
599
+            \OC::$server->getLogger()->logException($e, [
600
+                'message' => 'Could not set avatar for ' . $this->dn,
601
+                'level' => ILogger::INFO,
602
+                'app' => 'user_ldap',
603
+            ]);
604
+        }
605
+    }
606
+
607
+    /**
608
+     * called by a post_login hook to handle password expiry
609
+     *
610
+     * @param array $params
611
+     */
612
+    public function handlePasswordExpiry($params) {
613
+        $ppolicyDN = $this->connection->ldapDefaultPPolicyDN;
614
+        if (empty($ppolicyDN) || ((int)$this->connection->turnOnPasswordChange !== 1)) {
615
+            return;//password expiry handling disabled
616
+        }
617
+        $uid = $params['uid'];
618
+        if(isset($uid) && $uid === $this->getUsername()) {
619
+            //retrieve relevant user attributes
620
+            $result = $this->access->search('objectclass=*', $this->dn, ['pwdpolicysubentry', 'pwdgraceusetime', 'pwdreset', 'pwdchangedtime']);
621 621
 			
622
-			if(array_key_exists('pwdpolicysubentry', $result[0])) {
623
-				$pwdPolicySubentry = $result[0]['pwdpolicysubentry'];
624
-				if($pwdPolicySubentry && (count($pwdPolicySubentry) > 0)){
625
-					$ppolicyDN = $pwdPolicySubentry[0];//custom ppolicy DN
626
-				}
627
-			}
622
+            if(array_key_exists('pwdpolicysubentry', $result[0])) {
623
+                $pwdPolicySubentry = $result[0]['pwdpolicysubentry'];
624
+                if($pwdPolicySubentry && (count($pwdPolicySubentry) > 0)){
625
+                    $ppolicyDN = $pwdPolicySubentry[0];//custom ppolicy DN
626
+                }
627
+            }
628 628
 			
629
-			$pwdGraceUseTime = array_key_exists('pwdgraceusetime', $result[0]) ? $result[0]['pwdgraceusetime'] : null;
630
-			$pwdReset = array_key_exists('pwdreset', $result[0]) ? $result[0]['pwdreset'] : null;
631
-			$pwdChangedTime = array_key_exists('pwdchangedtime', $result[0]) ? $result[0]['pwdchangedtime'] : null;
629
+            $pwdGraceUseTime = array_key_exists('pwdgraceusetime', $result[0]) ? $result[0]['pwdgraceusetime'] : null;
630
+            $pwdReset = array_key_exists('pwdreset', $result[0]) ? $result[0]['pwdreset'] : null;
631
+            $pwdChangedTime = array_key_exists('pwdchangedtime', $result[0]) ? $result[0]['pwdchangedtime'] : null;
632 632
 			
633
-			//retrieve relevant password policy attributes
634
-			$cacheKey = 'ppolicyAttributes' . $ppolicyDN;
635
-			$result = $this->connection->getFromCache($cacheKey);
636
-			if(is_null($result)) {
637
-				$result = $this->access->search('objectclass=*', $ppolicyDN, ['pwdgraceauthnlimit', 'pwdmaxage', 'pwdexpirewarning']);
638
-				$this->connection->writeToCache($cacheKey, $result);
639
-			}
633
+            //retrieve relevant password policy attributes
634
+            $cacheKey = 'ppolicyAttributes' . $ppolicyDN;
635
+            $result = $this->connection->getFromCache($cacheKey);
636
+            if(is_null($result)) {
637
+                $result = $this->access->search('objectclass=*', $ppolicyDN, ['pwdgraceauthnlimit', 'pwdmaxage', 'pwdexpirewarning']);
638
+                $this->connection->writeToCache($cacheKey, $result);
639
+            }
640 640
 			
641
-			$pwdGraceAuthNLimit = array_key_exists('pwdgraceauthnlimit', $result[0]) ? $result[0]['pwdgraceauthnlimit'] : null;
642
-			$pwdMaxAge = array_key_exists('pwdmaxage', $result[0]) ? $result[0]['pwdmaxage'] : null;
643
-			$pwdExpireWarning = array_key_exists('pwdexpirewarning', $result[0]) ? $result[0]['pwdexpirewarning'] : null;
641
+            $pwdGraceAuthNLimit = array_key_exists('pwdgraceauthnlimit', $result[0]) ? $result[0]['pwdgraceauthnlimit'] : null;
642
+            $pwdMaxAge = array_key_exists('pwdmaxage', $result[0]) ? $result[0]['pwdmaxage'] : null;
643
+            $pwdExpireWarning = array_key_exists('pwdexpirewarning', $result[0]) ? $result[0]['pwdexpirewarning'] : null;
644 644
 			
645
-			//handle grace login
646
-			$pwdGraceUseTimeCount = count($pwdGraceUseTime);
647
-			if($pwdGraceUseTime && $pwdGraceUseTimeCount > 0) { //was this a grace login?
648
-				if($pwdGraceAuthNLimit 
649
-					&& (count($pwdGraceAuthNLimit) > 0)
650
-					&&($pwdGraceUseTimeCount < (int)$pwdGraceAuthNLimit[0])) { //at least one more grace login available?
651
-					$this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
652
-					header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
653
-					'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid)));
654
-				} else { //no more grace login available
655
-					header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
656
-					'user_ldap.renewPassword.showLoginFormInvalidPassword', array('user' => $uid)));
657
-				}
658
-				exit();
659
-			}
660
-			//handle pwdReset attribute
661
-			if($pwdReset && (count($pwdReset) > 0) && $pwdReset[0] === 'TRUE') { //user must change his password
662
-				$this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
663
-				header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
664
-				'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid)));
665
-				exit();
666
-			}
667
-			//handle password expiry warning
668
-			if($pwdChangedTime && (count($pwdChangedTime) > 0)) {
669
-				if($pwdMaxAge && (count($pwdMaxAge) > 0)
670
-					&& $pwdExpireWarning && (count($pwdExpireWarning) > 0)) {
671
-					$pwdMaxAgeInt = (int)$pwdMaxAge[0];
672
-					$pwdExpireWarningInt = (int)$pwdExpireWarning[0];
673
-					if($pwdMaxAgeInt > 0 && $pwdExpireWarningInt > 0){
674
-						$pwdChangedTimeDt = \DateTime::createFromFormat('YmdHisZ', $pwdChangedTime[0]);
675
-						$pwdChangedTimeDt->add(new \DateInterval('PT'.$pwdMaxAgeInt.'S'));
676
-						$currentDateTime = new \DateTime();
677
-						$secondsToExpiry = $pwdChangedTimeDt->getTimestamp() - $currentDateTime->getTimestamp();
678
-						if($secondsToExpiry <= $pwdExpireWarningInt) {
679
-							//remove last password expiry warning if any
680
-							$notification = $this->notificationManager->createNotification();
681
-							$notification->setApp('user_ldap')
682
-								->setUser($uid)
683
-								->setObject('pwd_exp_warn', $uid)
684
-							;
685
-							$this->notificationManager->markProcessed($notification);
686
-							//create new password expiry warning
687
-							$notification = $this->notificationManager->createNotification();
688
-							$notification->setApp('user_ldap')
689
-								->setUser($uid)
690
-								->setDateTime($currentDateTime)
691
-								->setObject('pwd_exp_warn', $uid) 
692
-								->setSubject('pwd_exp_warn_days', [(int) ceil($secondsToExpiry / 60 / 60 / 24)])
693
-							;
694
-							$this->notificationManager->notify($notification);
695
-						}
696
-					}
697
-				}
698
-			}
699
-		}
700
-	}
645
+            //handle grace login
646
+            $pwdGraceUseTimeCount = count($pwdGraceUseTime);
647
+            if($pwdGraceUseTime && $pwdGraceUseTimeCount > 0) { //was this a grace login?
648
+                if($pwdGraceAuthNLimit 
649
+                    && (count($pwdGraceAuthNLimit) > 0)
650
+                    &&($pwdGraceUseTimeCount < (int)$pwdGraceAuthNLimit[0])) { //at least one more grace login available?
651
+                    $this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
652
+                    header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
653
+                    'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid)));
654
+                } else { //no more grace login available
655
+                    header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
656
+                    'user_ldap.renewPassword.showLoginFormInvalidPassword', array('user' => $uid)));
657
+                }
658
+                exit();
659
+            }
660
+            //handle pwdReset attribute
661
+            if($pwdReset && (count($pwdReset) > 0) && $pwdReset[0] === 'TRUE') { //user must change his password
662
+                $this->config->setUserValue($uid, 'user_ldap', 'needsPasswordReset', 'true');
663
+                header('Location: '.\OC::$server->getURLGenerator()->linkToRouteAbsolute(
664
+                'user_ldap.renewPassword.showRenewPasswordForm', array('user' => $uid)));
665
+                exit();
666
+            }
667
+            //handle password expiry warning
668
+            if($pwdChangedTime && (count($pwdChangedTime) > 0)) {
669
+                if($pwdMaxAge && (count($pwdMaxAge) > 0)
670
+                    && $pwdExpireWarning && (count($pwdExpireWarning) > 0)) {
671
+                    $pwdMaxAgeInt = (int)$pwdMaxAge[0];
672
+                    $pwdExpireWarningInt = (int)$pwdExpireWarning[0];
673
+                    if($pwdMaxAgeInt > 0 && $pwdExpireWarningInt > 0){
674
+                        $pwdChangedTimeDt = \DateTime::createFromFormat('YmdHisZ', $pwdChangedTime[0]);
675
+                        $pwdChangedTimeDt->add(new \DateInterval('PT'.$pwdMaxAgeInt.'S'));
676
+                        $currentDateTime = new \DateTime();
677
+                        $secondsToExpiry = $pwdChangedTimeDt->getTimestamp() - $currentDateTime->getTimestamp();
678
+                        if($secondsToExpiry <= $pwdExpireWarningInt) {
679
+                            //remove last password expiry warning if any
680
+                            $notification = $this->notificationManager->createNotification();
681
+                            $notification->setApp('user_ldap')
682
+                                ->setUser($uid)
683
+                                ->setObject('pwd_exp_warn', $uid)
684
+                            ;
685
+                            $this->notificationManager->markProcessed($notification);
686
+                            //create new password expiry warning
687
+                            $notification = $this->notificationManager->createNotification();
688
+                            $notification->setApp('user_ldap')
689
+                                ->setUser($uid)
690
+                                ->setDateTime($currentDateTime)
691
+                                ->setObject('pwd_exp_warn', $uid) 
692
+                                ->setSubject('pwd_exp_warn_days', [(int) ceil($secondsToExpiry / 60 / 60 / 24)])
693
+                            ;
694
+                            $this->notificationManager->notify($notification);
695
+                        }
696
+                    }
697
+                }
698
+            }
699
+        }
700
+    }
701 701
 }
Please login to merge, or discard this patch.
apps/dav/lib/Connector/Sabre/ExceptionLoggerPlugin.php 1 patch
Indentation   +76 added lines, -76 removed lines patch added patch discarded remove patch
@@ -40,87 +40,87 @@
 block discarded – undo
40 40
 use Sabre\DAV\Exception\ServiceUnavailable;
41 41
 
42 42
 class ExceptionLoggerPlugin extends \Sabre\DAV\ServerPlugin {
43
-	protected $nonFatalExceptions = [
44
-		NotAuthenticated::class => true,
45
-		// If tokenauth can throw this exception (which is basically as
46
-		// NotAuthenticated. So not fatal.
47
-		PasswordLoginForbidden::class => true,
48
-		// basically a NotAuthenticated
49
-		InvalidSyncToken::class => true,
50
-		// the sync client uses this to find out whether files exist,
51
-		// so it is not always an error, log it as debug
52
-		NotFound::class => true,
53
-		// this one mostly happens when the same file is uploaded at
54
-		// exactly the same time from two clients, only one client
55
-		// wins, the second one gets "Precondition failed"
56
-		PreconditionFailed::class => true,
57
-		// forbidden can be expected when trying to upload to
58
-		// read-only folders for example
59
-		Forbidden::class => true,
60
-		// Happens when an external storage or federated share is temporarily
61
-		// not available
62
-		StorageNotAvailableException::class => true,
63
-		// happens if some a client uses the wrong method for a given URL
64
-		// the error message itself is visible on the client side anyways
65
-		NotImplemented::class => true,
66
-		// happens when the parent directory is not present (for example when a
67
-		// move is done to a non-existent directory)
68
-		Conflict::class => true,
69
-		// happens when a certain method is not allowed to be called
70
-		// for example creating a folder that already exists
71
-		MethodNotAllowed::class => true,
72
-	];
43
+    protected $nonFatalExceptions = [
44
+        NotAuthenticated::class => true,
45
+        // If tokenauth can throw this exception (which is basically as
46
+        // NotAuthenticated. So not fatal.
47
+        PasswordLoginForbidden::class => true,
48
+        // basically a NotAuthenticated
49
+        InvalidSyncToken::class => true,
50
+        // the sync client uses this to find out whether files exist,
51
+        // so it is not always an error, log it as debug
52
+        NotFound::class => true,
53
+        // this one mostly happens when the same file is uploaded at
54
+        // exactly the same time from two clients, only one client
55
+        // wins, the second one gets "Precondition failed"
56
+        PreconditionFailed::class => true,
57
+        // forbidden can be expected when trying to upload to
58
+        // read-only folders for example
59
+        Forbidden::class => true,
60
+        // Happens when an external storage or federated share is temporarily
61
+        // not available
62
+        StorageNotAvailableException::class => true,
63
+        // happens if some a client uses the wrong method for a given URL
64
+        // the error message itself is visible on the client side anyways
65
+        NotImplemented::class => true,
66
+        // happens when the parent directory is not present (for example when a
67
+        // move is done to a non-existent directory)
68
+        Conflict::class => true,
69
+        // happens when a certain method is not allowed to be called
70
+        // for example creating a folder that already exists
71
+        MethodNotAllowed::class => true,
72
+    ];
73 73
 
74
-	/** @var string */
75
-	private $appName;
74
+    /** @var string */
75
+    private $appName;
76 76
 
77
-	/** @var ILogger */
78
-	private $logger;
77
+    /** @var ILogger */
78
+    private $logger;
79 79
 
80
-	/**
81
-	 * @param string $loggerAppName app name to use when logging
82
-	 * @param ILogger $logger
83
-	 */
84
-	public function __construct($loggerAppName, $logger) {
85
-		$this->appName = $loggerAppName;
86
-		$this->logger = $logger;
87
-	}
80
+    /**
81
+     * @param string $loggerAppName app name to use when logging
82
+     * @param ILogger $logger
83
+     */
84
+    public function __construct($loggerAppName, $logger) {
85
+        $this->appName = $loggerAppName;
86
+        $this->logger = $logger;
87
+    }
88 88
 
89
-	/**
90
-	 * This initializes the plugin.
91
-	 *
92
-	 * This function is called by \Sabre\DAV\Server, after
93
-	 * addPlugin is called.
94
-	 *
95
-	 * This method should set up the required event subscriptions.
96
-	 *
97
-	 * @param \Sabre\DAV\Server $server
98
-	 * @return void
99
-	 */
100
-	public function initialize(\Sabre\DAV\Server $server) {
89
+    /**
90
+     * This initializes the plugin.
91
+     *
92
+     * This function is called by \Sabre\DAV\Server, after
93
+     * addPlugin is called.
94
+     *
95
+     * This method should set up the required event subscriptions.
96
+     *
97
+     * @param \Sabre\DAV\Server $server
98
+     * @return void
99
+     */
100
+    public function initialize(\Sabre\DAV\Server $server) {
101 101
 
102
-		$server->on('exception', array($this, 'logException'), 10);
103
-	}
102
+        $server->on('exception', array($this, 'logException'), 10);
103
+    }
104 104
 
105
-	/**
106
-	 * Log exception
107
-	 *
108
-	 */
109
-	public function logException(\Exception $ex) {
110
-		$exceptionClass = get_class($ex);
111
-		$level = ILogger::FATAL;
112
-		if (isset($this->nonFatalExceptions[$exceptionClass]) ||
113
-			(
114
-				$exceptionClass === ServiceUnavailable::class &&
115
-				$ex->getMessage() === 'System in maintenance mode.'
116
-			)
117
-		) {
118
-			$level = ILogger::DEBUG;
119
-		}
105
+    /**
106
+     * Log exception
107
+     *
108
+     */
109
+    public function logException(\Exception $ex) {
110
+        $exceptionClass = get_class($ex);
111
+        $level = ILogger::FATAL;
112
+        if (isset($this->nonFatalExceptions[$exceptionClass]) ||
113
+            (
114
+                $exceptionClass === ServiceUnavailable::class &&
115
+                $ex->getMessage() === 'System in maintenance mode.'
116
+            )
117
+        ) {
118
+            $level = ILogger::DEBUG;
119
+        }
120 120
 
121
-		$this->logger->logException($ex, [
122
-			'app' => $this->appName,
123
-			'level' => $level,
124
-		]);
125
-	}
121
+        $this->logger->logException($ex, [
122
+            'app' => $this->appName,
123
+            'level' => $level,
124
+        ]);
125
+    }
126 126
 }
Please login to merge, or discard this patch.
apps/federatedfilesharing/lib/FederatedShareProvider.php 1 patch
Indentation   +975 added lines, -975 removed lines patch added patch discarded remove patch
@@ -53,989 +53,989 @@
 block discarded – undo
53 53
  */
54 54
 class FederatedShareProvider implements IShareProvider {
55 55
 
56
-	const SHARE_TYPE_REMOTE = 6;
57
-
58
-	/** @var IDBConnection */
59
-	private $dbConnection;
60
-
61
-	/** @var AddressHandler */
62
-	private $addressHandler;
63
-
64
-	/** @var Notifications */
65
-	private $notifications;
66
-
67
-	/** @var TokenHandler */
68
-	private $tokenHandler;
69
-
70
-	/** @var IL10N */
71
-	private $l;
72
-
73
-	/** @var ILogger */
74
-	private $logger;
75
-
76
-	/** @var IRootFolder */
77
-	private $rootFolder;
78
-
79
-	/** @var IConfig */
80
-	private $config;
81
-
82
-	/** @var string */
83
-	private $externalShareTable = 'share_external';
84
-
85
-	/** @var IUserManager */
86
-	private $userManager;
87
-
88
-	/** @var ICloudIdManager */
89
-	private $cloudIdManager;
90
-
91
-	/** @var \OCP\GlobalScale\IConfig */
92
-	private $gsConfig;
93
-
94
-	/**
95
-	 * DefaultShareProvider constructor.
96
-	 *
97
-	 * @param IDBConnection $connection
98
-	 * @param AddressHandler $addressHandler
99
-	 * @param Notifications $notifications
100
-	 * @param TokenHandler $tokenHandler
101
-	 * @param IL10N $l10n
102
-	 * @param ILogger $logger
103
-	 * @param IRootFolder $rootFolder
104
-	 * @param IConfig $config
105
-	 * @param IUserManager $userManager
106
-	 * @param ICloudIdManager $cloudIdManager
107
-	 * @param \OCP\GlobalScale\IConfig $globalScaleConfig
108
-	 */
109
-	public function __construct(
110
-			IDBConnection $connection,
111
-			AddressHandler $addressHandler,
112
-			Notifications $notifications,
113
-			TokenHandler $tokenHandler,
114
-			IL10N $l10n,
115
-			ILogger $logger,
116
-			IRootFolder $rootFolder,
117
-			IConfig $config,
118
-			IUserManager $userManager,
119
-			ICloudIdManager $cloudIdManager,
120
-			\OCP\GlobalScale\IConfig $globalScaleConfig
121
-	) {
122
-		$this->dbConnection = $connection;
123
-		$this->addressHandler = $addressHandler;
124
-		$this->notifications = $notifications;
125
-		$this->tokenHandler = $tokenHandler;
126
-		$this->l = $l10n;
127
-		$this->logger = $logger;
128
-		$this->rootFolder = $rootFolder;
129
-		$this->config = $config;
130
-		$this->userManager = $userManager;
131
-		$this->cloudIdManager = $cloudIdManager;
132
-		$this->gsConfig = $globalScaleConfig;
133
-	}
134
-
135
-	/**
136
-	 * Return the identifier of this provider.
137
-	 *
138
-	 * @return string Containing only [a-zA-Z0-9]
139
-	 */
140
-	public function identifier() {
141
-		return 'ocFederatedSharing';
142
-	}
143
-
144
-	/**
145
-	 * Share a path
146
-	 *
147
-	 * @param IShare $share
148
-	 * @return IShare The share object
149
-	 * @throws ShareNotFound
150
-	 * @throws \Exception
151
-	 */
152
-	public function create(IShare $share) {
153
-
154
-		$shareWith = $share->getSharedWith();
155
-		$itemSource = $share->getNodeId();
156
-		$itemType = $share->getNodeType();
157
-		$permissions = $share->getPermissions();
158
-		$sharedBy = $share->getSharedBy();
159
-
160
-		/*
56
+    const SHARE_TYPE_REMOTE = 6;
57
+
58
+    /** @var IDBConnection */
59
+    private $dbConnection;
60
+
61
+    /** @var AddressHandler */
62
+    private $addressHandler;
63
+
64
+    /** @var Notifications */
65
+    private $notifications;
66
+
67
+    /** @var TokenHandler */
68
+    private $tokenHandler;
69
+
70
+    /** @var IL10N */
71
+    private $l;
72
+
73
+    /** @var ILogger */
74
+    private $logger;
75
+
76
+    /** @var IRootFolder */
77
+    private $rootFolder;
78
+
79
+    /** @var IConfig */
80
+    private $config;
81
+
82
+    /** @var string */
83
+    private $externalShareTable = 'share_external';
84
+
85
+    /** @var IUserManager */
86
+    private $userManager;
87
+
88
+    /** @var ICloudIdManager */
89
+    private $cloudIdManager;
90
+
91
+    /** @var \OCP\GlobalScale\IConfig */
92
+    private $gsConfig;
93
+
94
+    /**
95
+     * DefaultShareProvider constructor.
96
+     *
97
+     * @param IDBConnection $connection
98
+     * @param AddressHandler $addressHandler
99
+     * @param Notifications $notifications
100
+     * @param TokenHandler $tokenHandler
101
+     * @param IL10N $l10n
102
+     * @param ILogger $logger
103
+     * @param IRootFolder $rootFolder
104
+     * @param IConfig $config
105
+     * @param IUserManager $userManager
106
+     * @param ICloudIdManager $cloudIdManager
107
+     * @param \OCP\GlobalScale\IConfig $globalScaleConfig
108
+     */
109
+    public function __construct(
110
+            IDBConnection $connection,
111
+            AddressHandler $addressHandler,
112
+            Notifications $notifications,
113
+            TokenHandler $tokenHandler,
114
+            IL10N $l10n,
115
+            ILogger $logger,
116
+            IRootFolder $rootFolder,
117
+            IConfig $config,
118
+            IUserManager $userManager,
119
+            ICloudIdManager $cloudIdManager,
120
+            \OCP\GlobalScale\IConfig $globalScaleConfig
121
+    ) {
122
+        $this->dbConnection = $connection;
123
+        $this->addressHandler = $addressHandler;
124
+        $this->notifications = $notifications;
125
+        $this->tokenHandler = $tokenHandler;
126
+        $this->l = $l10n;
127
+        $this->logger = $logger;
128
+        $this->rootFolder = $rootFolder;
129
+        $this->config = $config;
130
+        $this->userManager = $userManager;
131
+        $this->cloudIdManager = $cloudIdManager;
132
+        $this->gsConfig = $globalScaleConfig;
133
+    }
134
+
135
+    /**
136
+     * Return the identifier of this provider.
137
+     *
138
+     * @return string Containing only [a-zA-Z0-9]
139
+     */
140
+    public function identifier() {
141
+        return 'ocFederatedSharing';
142
+    }
143
+
144
+    /**
145
+     * Share a path
146
+     *
147
+     * @param IShare $share
148
+     * @return IShare The share object
149
+     * @throws ShareNotFound
150
+     * @throws \Exception
151
+     */
152
+    public function create(IShare $share) {
153
+
154
+        $shareWith = $share->getSharedWith();
155
+        $itemSource = $share->getNodeId();
156
+        $itemType = $share->getNodeType();
157
+        $permissions = $share->getPermissions();
158
+        $sharedBy = $share->getSharedBy();
159
+
160
+        /*
161 161
 		 * Check if file is not already shared with the remote user
162 162
 		 */
163
-		$alreadyShared = $this->getSharedWith($shareWith, self::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0);
164
-		if (!empty($alreadyShared)) {
165
-			$message = 'Sharing %s failed, because this item is already shared with %s';
166
-			$message_t = $this->l->t('Sharing %s failed, because this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
167
-			$this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
168
-			throw new \Exception($message_t);
169
-		}
170
-
171
-
172
-		// don't allow federated shares if source and target server are the same
173
-		$cloudId = $this->cloudIdManager->resolveCloudId($shareWith);
174
-		$currentServer = $this->addressHandler->generateRemoteURL();
175
-		$currentUser = $sharedBy;
176
-		if ($this->addressHandler->compareAddresses($cloudId->getUser(), $cloudId->getRemote(), $currentUser, $currentServer)) {
177
-			$message = 'Not allowed to create a federated share with the same user.';
178
-			$message_t = $this->l->t('Not allowed to create a federated share with the same user');
179
-			$this->logger->debug($message, ['app' => 'Federated File Sharing']);
180
-			throw new \Exception($message_t);
181
-		}
182
-
183
-
184
-		$share->setSharedWith($cloudId->getId());
185
-
186
-		try {
187
-			$remoteShare = $this->getShareFromExternalShareTable($share);
188
-		} catch (ShareNotFound $e) {
189
-			$remoteShare = null;
190
-		}
191
-
192
-		if ($remoteShare) {
193
-			try {
194
-				$ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']);
195
-				$shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time());
196
-				$share->setId($shareId);
197
-				list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId);
198
-				// remote share was create successfully if we get a valid token as return
199
-				$send = is_string($token) && $token !== '';
200
-			} catch (\Exception $e) {
201
-				// fall back to old re-share behavior if the remote server
202
-				// doesn't support flat re-shares (was introduced with Nextcloud 9.1)
203
-				$this->removeShareFromTable($share);
204
-				$shareId = $this->createFederatedShare($share);
205
-			}
206
-			if ($send) {
207
-				$this->updateSuccessfulReshare($shareId, $token);
208
-				$this->storeRemoteId($shareId, $remoteId);
209
-			} else {
210
-				$this->removeShareFromTable($share);
211
-				$message_t = $this->l->t('File is already shared with %s', [$shareWith]);
212
-				throw new \Exception($message_t);
213
-			}
214
-
215
-		} else {
216
-			$shareId = $this->createFederatedShare($share);
217
-		}
218
-
219
-		$data = $this->getRawShare($shareId);
220
-		return $this->createShareObject($data);
221
-	}
222
-
223
-	/**
224
-	 * create federated share and inform the recipient
225
-	 *
226
-	 * @param IShare $share
227
-	 * @return int
228
-	 * @throws ShareNotFound
229
-	 * @throws \Exception
230
-	 */
231
-	protected function createFederatedShare(IShare $share) {
232
-		$token = $this->tokenHandler->generateToken();
233
-		$shareId = $this->addShareToDB(
234
-			$share->getNodeId(),
235
-			$share->getNodeType(),
236
-			$share->getSharedWith(),
237
-			$share->getSharedBy(),
238
-			$share->getShareOwner(),
239
-			$share->getPermissions(),
240
-			$token
241
-		);
242
-
243
-		$failure = false;
244
-
245
-		try {
246
-			$sharedByFederatedId = $share->getSharedBy();
247
-			if ($this->userManager->userExists($sharedByFederatedId)) {
248
-				$cloudId = $this->cloudIdManager->getCloudId($sharedByFederatedId, $this->addressHandler->generateRemoteURL());
249
-				$sharedByFederatedId = $cloudId->getId();
250
-			}
251
-			$ownerCloudId = $this->cloudIdManager->getCloudId($share->getShareOwner(), $this->addressHandler->generateRemoteURL());
252
-			$send = $this->notifications->sendRemoteShare(
253
-				$token,
254
-				$share->getSharedWith(),
255
-				$share->getNode()->getName(),
256
-				$shareId,
257
-				$share->getShareOwner(),
258
-				$ownerCloudId->getId(),
259
-				$share->getSharedBy(),
260
-				$sharedByFederatedId
261
-			);
262
-
263
-			if ($send === false) {
264
-				$failure = true;
265
-			}
266
-		} catch (\Exception $e) {
267
-			$this->logger->logException($e, [
268
-				'message' => 'Failed to notify remote server of federated share, removing share.',
269
-				'level' => ILogger::ERROR,
270
-				'app' => 'federatedfilesharing',
271
-			]);
272
-			$failure = true;
273
-		}
274
-
275
-		if($failure) {
276
-			$this->removeShareFromTableById($shareId);
277
-			$message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable or uses a self-signed certificate.',
278
-				[$share->getNode()->getName(), $share->getSharedWith()]);
279
-			throw new \Exception($message_t);
280
-		}
281
-
282
-		return $shareId;
283
-
284
-	}
285
-
286
-	/**
287
-	 * @param string $shareWith
288
-	 * @param IShare $share
289
-	 * @param string $shareId internal share Id
290
-	 * @return array
291
-	 * @throws \Exception
292
-	 */
293
-	protected function askOwnerToReShare($shareWith, IShare $share, $shareId) {
294
-
295
-		$remoteShare = $this->getShareFromExternalShareTable($share);
296
-		$token = $remoteShare['share_token'];
297
-		$remoteId = $remoteShare['remote_id'];
298
-		$remote = $remoteShare['remote'];
299
-
300
-		list($token, $remoteId) = $this->notifications->requestReShare(
301
-			$token,
302
-			$remoteId,
303
-			$shareId,
304
-			$remote,
305
-			$shareWith,
306
-			$share->getPermissions()
307
-		);
308
-
309
-		return [$token, $remoteId];
310
-	}
311
-
312
-	/**
313
-	 * get federated share from the share_external table but exclude mounted link shares
314
-	 *
315
-	 * @param IShare $share
316
-	 * @return array
317
-	 * @throws ShareNotFound
318
-	 */
319
-	protected function getShareFromExternalShareTable(IShare $share) {
320
-		$query = $this->dbConnection->getQueryBuilder();
321
-		$query->select('*')->from($this->externalShareTable)
322
-			->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner())))
323
-			->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget())));
324
-		$result = $query->execute()->fetchAll();
325
-
326
-		if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) {
327
-			return $result[0];
328
-		}
329
-
330
-		throw new ShareNotFound('share not found in share_external table');
331
-	}
332
-
333
-	/**
334
-	 * add share to the database and return the ID
335
-	 *
336
-	 * @param int $itemSource
337
-	 * @param string $itemType
338
-	 * @param string $shareWith
339
-	 * @param string $sharedBy
340
-	 * @param string $uidOwner
341
-	 * @param int $permissions
342
-	 * @param string $token
343
-	 * @return int
344
-	 */
345
-	private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) {
346
-		$qb = $this->dbConnection->getQueryBuilder();
347
-		$qb->insert('share')
348
-			->setValue('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))
349
-			->setValue('item_type', $qb->createNamedParameter($itemType))
350
-			->setValue('item_source', $qb->createNamedParameter($itemSource))
351
-			->setValue('file_source', $qb->createNamedParameter($itemSource))
352
-			->setValue('share_with', $qb->createNamedParameter($shareWith))
353
-			->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
354
-			->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
355
-			->setValue('permissions', $qb->createNamedParameter($permissions))
356
-			->setValue('token', $qb->createNamedParameter($token))
357
-			->setValue('stime', $qb->createNamedParameter(time()));
358
-
359
-		/*
163
+        $alreadyShared = $this->getSharedWith($shareWith, self::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0);
164
+        if (!empty($alreadyShared)) {
165
+            $message = 'Sharing %s failed, because this item is already shared with %s';
166
+            $message_t = $this->l->t('Sharing %s failed, because this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
167
+            $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
168
+            throw new \Exception($message_t);
169
+        }
170
+
171
+
172
+        // don't allow federated shares if source and target server are the same
173
+        $cloudId = $this->cloudIdManager->resolveCloudId($shareWith);
174
+        $currentServer = $this->addressHandler->generateRemoteURL();
175
+        $currentUser = $sharedBy;
176
+        if ($this->addressHandler->compareAddresses($cloudId->getUser(), $cloudId->getRemote(), $currentUser, $currentServer)) {
177
+            $message = 'Not allowed to create a federated share with the same user.';
178
+            $message_t = $this->l->t('Not allowed to create a federated share with the same user');
179
+            $this->logger->debug($message, ['app' => 'Federated File Sharing']);
180
+            throw new \Exception($message_t);
181
+        }
182
+
183
+
184
+        $share->setSharedWith($cloudId->getId());
185
+
186
+        try {
187
+            $remoteShare = $this->getShareFromExternalShareTable($share);
188
+        } catch (ShareNotFound $e) {
189
+            $remoteShare = null;
190
+        }
191
+
192
+        if ($remoteShare) {
193
+            try {
194
+                $ownerCloudId = $this->cloudIdManager->getCloudId($remoteShare['owner'], $remoteShare['remote']);
195
+                $shareId = $this->addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $ownerCloudId->getId(), $permissions, 'tmp_token_' . time());
196
+                $share->setId($shareId);
197
+                list($token, $remoteId) = $this->askOwnerToReShare($shareWith, $share, $shareId);
198
+                // remote share was create successfully if we get a valid token as return
199
+                $send = is_string($token) && $token !== '';
200
+            } catch (\Exception $e) {
201
+                // fall back to old re-share behavior if the remote server
202
+                // doesn't support flat re-shares (was introduced with Nextcloud 9.1)
203
+                $this->removeShareFromTable($share);
204
+                $shareId = $this->createFederatedShare($share);
205
+            }
206
+            if ($send) {
207
+                $this->updateSuccessfulReshare($shareId, $token);
208
+                $this->storeRemoteId($shareId, $remoteId);
209
+            } else {
210
+                $this->removeShareFromTable($share);
211
+                $message_t = $this->l->t('File is already shared with %s', [$shareWith]);
212
+                throw new \Exception($message_t);
213
+            }
214
+
215
+        } else {
216
+            $shareId = $this->createFederatedShare($share);
217
+        }
218
+
219
+        $data = $this->getRawShare($shareId);
220
+        return $this->createShareObject($data);
221
+    }
222
+
223
+    /**
224
+     * create federated share and inform the recipient
225
+     *
226
+     * @param IShare $share
227
+     * @return int
228
+     * @throws ShareNotFound
229
+     * @throws \Exception
230
+     */
231
+    protected function createFederatedShare(IShare $share) {
232
+        $token = $this->tokenHandler->generateToken();
233
+        $shareId = $this->addShareToDB(
234
+            $share->getNodeId(),
235
+            $share->getNodeType(),
236
+            $share->getSharedWith(),
237
+            $share->getSharedBy(),
238
+            $share->getShareOwner(),
239
+            $share->getPermissions(),
240
+            $token
241
+        );
242
+
243
+        $failure = false;
244
+
245
+        try {
246
+            $sharedByFederatedId = $share->getSharedBy();
247
+            if ($this->userManager->userExists($sharedByFederatedId)) {
248
+                $cloudId = $this->cloudIdManager->getCloudId($sharedByFederatedId, $this->addressHandler->generateRemoteURL());
249
+                $sharedByFederatedId = $cloudId->getId();
250
+            }
251
+            $ownerCloudId = $this->cloudIdManager->getCloudId($share->getShareOwner(), $this->addressHandler->generateRemoteURL());
252
+            $send = $this->notifications->sendRemoteShare(
253
+                $token,
254
+                $share->getSharedWith(),
255
+                $share->getNode()->getName(),
256
+                $shareId,
257
+                $share->getShareOwner(),
258
+                $ownerCloudId->getId(),
259
+                $share->getSharedBy(),
260
+                $sharedByFederatedId
261
+            );
262
+
263
+            if ($send === false) {
264
+                $failure = true;
265
+            }
266
+        } catch (\Exception $e) {
267
+            $this->logger->logException($e, [
268
+                'message' => 'Failed to notify remote server of federated share, removing share.',
269
+                'level' => ILogger::ERROR,
270
+                'app' => 'federatedfilesharing',
271
+            ]);
272
+            $failure = true;
273
+        }
274
+
275
+        if($failure) {
276
+            $this->removeShareFromTableById($shareId);
277
+            $message_t = $this->l->t('Sharing %s failed, could not find %s, maybe the server is currently unreachable or uses a self-signed certificate.',
278
+                [$share->getNode()->getName(), $share->getSharedWith()]);
279
+            throw new \Exception($message_t);
280
+        }
281
+
282
+        return $shareId;
283
+
284
+    }
285
+
286
+    /**
287
+     * @param string $shareWith
288
+     * @param IShare $share
289
+     * @param string $shareId internal share Id
290
+     * @return array
291
+     * @throws \Exception
292
+     */
293
+    protected function askOwnerToReShare($shareWith, IShare $share, $shareId) {
294
+
295
+        $remoteShare = $this->getShareFromExternalShareTable($share);
296
+        $token = $remoteShare['share_token'];
297
+        $remoteId = $remoteShare['remote_id'];
298
+        $remote = $remoteShare['remote'];
299
+
300
+        list($token, $remoteId) = $this->notifications->requestReShare(
301
+            $token,
302
+            $remoteId,
303
+            $shareId,
304
+            $remote,
305
+            $shareWith,
306
+            $share->getPermissions()
307
+        );
308
+
309
+        return [$token, $remoteId];
310
+    }
311
+
312
+    /**
313
+     * get federated share from the share_external table but exclude mounted link shares
314
+     *
315
+     * @param IShare $share
316
+     * @return array
317
+     * @throws ShareNotFound
318
+     */
319
+    protected function getShareFromExternalShareTable(IShare $share) {
320
+        $query = $this->dbConnection->getQueryBuilder();
321
+        $query->select('*')->from($this->externalShareTable)
322
+            ->where($query->expr()->eq('user', $query->createNamedParameter($share->getShareOwner())))
323
+            ->andWhere($query->expr()->eq('mountpoint', $query->createNamedParameter($share->getTarget())));
324
+        $result = $query->execute()->fetchAll();
325
+
326
+        if (isset($result[0]) && (int)$result[0]['remote_id'] > 0) {
327
+            return $result[0];
328
+        }
329
+
330
+        throw new ShareNotFound('share not found in share_external table');
331
+    }
332
+
333
+    /**
334
+     * add share to the database and return the ID
335
+     *
336
+     * @param int $itemSource
337
+     * @param string $itemType
338
+     * @param string $shareWith
339
+     * @param string $sharedBy
340
+     * @param string $uidOwner
341
+     * @param int $permissions
342
+     * @param string $token
343
+     * @return int
344
+     */
345
+    private function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) {
346
+        $qb = $this->dbConnection->getQueryBuilder();
347
+        $qb->insert('share')
348
+            ->setValue('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE))
349
+            ->setValue('item_type', $qb->createNamedParameter($itemType))
350
+            ->setValue('item_source', $qb->createNamedParameter($itemSource))
351
+            ->setValue('file_source', $qb->createNamedParameter($itemSource))
352
+            ->setValue('share_with', $qb->createNamedParameter($shareWith))
353
+            ->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
354
+            ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
355
+            ->setValue('permissions', $qb->createNamedParameter($permissions))
356
+            ->setValue('token', $qb->createNamedParameter($token))
357
+            ->setValue('stime', $qb->createNamedParameter(time()));
358
+
359
+        /*
360 360
 		 * Added to fix https://github.com/owncloud/core/issues/22215
361 361
 		 * Can be removed once we get rid of ajax/share.php
362 362
 		 */
363
-		$qb->setValue('file_target', $qb->createNamedParameter(''));
364
-
365
-		$qb->execute();
366
-		$id = $qb->getLastInsertId();
367
-
368
-		return (int)$id;
369
-	}
370
-
371
-	/**
372
-	 * Update a share
373
-	 *
374
-	 * @param IShare $share
375
-	 * @return IShare The share object
376
-	 */
377
-	public function update(IShare $share) {
378
-		/*
363
+        $qb->setValue('file_target', $qb->createNamedParameter(''));
364
+
365
+        $qb->execute();
366
+        $id = $qb->getLastInsertId();
367
+
368
+        return (int)$id;
369
+    }
370
+
371
+    /**
372
+     * Update a share
373
+     *
374
+     * @param IShare $share
375
+     * @return IShare The share object
376
+     */
377
+    public function update(IShare $share) {
378
+        /*
379 379
 		 * We allow updating the permissions of federated shares
380 380
 		 */
381
-		$qb = $this->dbConnection->getQueryBuilder();
382
-			$qb->update('share')
383
-				->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
384
-				->set('permissions', $qb->createNamedParameter($share->getPermissions()))
385
-				->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
386
-				->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
387
-				->execute();
388
-
389
-		// send the updated permission to the owner/initiator, if they are not the same
390
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
391
-			$this->sendPermissionUpdate($share);
392
-		}
393
-
394
-		return $share;
395
-	}
396
-
397
-	/**
398
-	 * send the updated permission to the owner/initiator, if they are not the same
399
-	 *
400
-	 * @param IShare $share
401
-	 * @throws ShareNotFound
402
-	 * @throws \OC\HintException
403
-	 */
404
-	protected function sendPermissionUpdate(IShare $share) {
405
-		$remoteId = $this->getRemoteId($share);
406
-		// if the local user is the owner we send the permission change to the initiator
407
-		if ($this->userManager->userExists($share->getShareOwner())) {
408
-			list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
409
-		} else { // ... if not we send the permission change to the owner
410
-			list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
411
-		}
412
-		$this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions());
413
-	}
414
-
415
-
416
-	/**
417
-	 * update successful reShare with the correct token
418
-	 *
419
-	 * @param int $shareId
420
-	 * @param string $token
421
-	 */
422
-	protected function updateSuccessfulReShare($shareId, $token) {
423
-		$query = $this->dbConnection->getQueryBuilder();
424
-		$query->update('share')
425
-			->where($query->expr()->eq('id', $query->createNamedParameter($shareId)))
426
-			->set('token', $query->createNamedParameter($token))
427
-			->execute();
428
-	}
429
-
430
-	/**
431
-	 * store remote ID in federated reShare table
432
-	 *
433
-	 * @param $shareId
434
-	 * @param $remoteId
435
-	 */
436
-	public function storeRemoteId($shareId, $remoteId) {
437
-		$query = $this->dbConnection->getQueryBuilder();
438
-		$query->insert('federated_reshares')
439
-			->values(
440
-				[
441
-					'share_id' =>  $query->createNamedParameter($shareId),
442
-					'remote_id' => $query->createNamedParameter($remoteId),
443
-				]
444
-			);
445
-		$query->execute();
446
-	}
447
-
448
-	/**
449
-	 * get share ID on remote server for federated re-shares
450
-	 *
451
-	 * @param IShare $share
452
-	 * @return int
453
-	 * @throws ShareNotFound
454
-	 */
455
-	public function getRemoteId(IShare $share) {
456
-		$query = $this->dbConnection->getQueryBuilder();
457
-		$query->select('remote_id')->from('federated_reshares')
458
-			->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId())));
459
-		$data = $query->execute()->fetch();
460
-
461
-		if (!is_array($data) || !isset($data['remote_id'])) {
462
-			throw new ShareNotFound();
463
-		}
464
-
465
-		return (int)$data['remote_id'];
466
-	}
467
-
468
-	/**
469
-	 * @inheritdoc
470
-	 */
471
-	public function move(IShare $share, $recipient) {
472
-		/*
381
+        $qb = $this->dbConnection->getQueryBuilder();
382
+            $qb->update('share')
383
+                ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
384
+                ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
385
+                ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
386
+                ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
387
+                ->execute();
388
+
389
+        // send the updated permission to the owner/initiator, if they are not the same
390
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
391
+            $this->sendPermissionUpdate($share);
392
+        }
393
+
394
+        return $share;
395
+    }
396
+
397
+    /**
398
+     * send the updated permission to the owner/initiator, if they are not the same
399
+     *
400
+     * @param IShare $share
401
+     * @throws ShareNotFound
402
+     * @throws \OC\HintException
403
+     */
404
+    protected function sendPermissionUpdate(IShare $share) {
405
+        $remoteId = $this->getRemoteId($share);
406
+        // if the local user is the owner we send the permission change to the initiator
407
+        if ($this->userManager->userExists($share->getShareOwner())) {
408
+            list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
409
+        } else { // ... if not we send the permission change to the owner
410
+            list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
411
+        }
412
+        $this->notifications->sendPermissionChange($remote, $remoteId, $share->getToken(), $share->getPermissions());
413
+    }
414
+
415
+
416
+    /**
417
+     * update successful reShare with the correct token
418
+     *
419
+     * @param int $shareId
420
+     * @param string $token
421
+     */
422
+    protected function updateSuccessfulReShare($shareId, $token) {
423
+        $query = $this->dbConnection->getQueryBuilder();
424
+        $query->update('share')
425
+            ->where($query->expr()->eq('id', $query->createNamedParameter($shareId)))
426
+            ->set('token', $query->createNamedParameter($token))
427
+            ->execute();
428
+    }
429
+
430
+    /**
431
+     * store remote ID in federated reShare table
432
+     *
433
+     * @param $shareId
434
+     * @param $remoteId
435
+     */
436
+    public function storeRemoteId($shareId, $remoteId) {
437
+        $query = $this->dbConnection->getQueryBuilder();
438
+        $query->insert('federated_reshares')
439
+            ->values(
440
+                [
441
+                    'share_id' =>  $query->createNamedParameter($shareId),
442
+                    'remote_id' => $query->createNamedParameter($remoteId),
443
+                ]
444
+            );
445
+        $query->execute();
446
+    }
447
+
448
+    /**
449
+     * get share ID on remote server for federated re-shares
450
+     *
451
+     * @param IShare $share
452
+     * @return int
453
+     * @throws ShareNotFound
454
+     */
455
+    public function getRemoteId(IShare $share) {
456
+        $query = $this->dbConnection->getQueryBuilder();
457
+        $query->select('remote_id')->from('federated_reshares')
458
+            ->where($query->expr()->eq('share_id', $query->createNamedParameter((int)$share->getId())));
459
+        $data = $query->execute()->fetch();
460
+
461
+        if (!is_array($data) || !isset($data['remote_id'])) {
462
+            throw new ShareNotFound();
463
+        }
464
+
465
+        return (int)$data['remote_id'];
466
+    }
467
+
468
+    /**
469
+     * @inheritdoc
470
+     */
471
+    public function move(IShare $share, $recipient) {
472
+        /*
473 473
 		 * This function does nothing yet as it is just for outgoing
474 474
 		 * federated shares.
475 475
 		 */
476
-		return $share;
477
-	}
478
-
479
-	/**
480
-	 * Get all children of this share
481
-	 *
482
-	 * @param IShare $parent
483
-	 * @return IShare[]
484
-	 */
485
-	public function getChildren(IShare $parent) {
486
-		$children = [];
487
-
488
-		$qb = $this->dbConnection->getQueryBuilder();
489
-		$qb->select('*')
490
-			->from('share')
491
-			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
492
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
493
-			->orderBy('id');
494
-
495
-		$cursor = $qb->execute();
496
-		while($data = $cursor->fetch()) {
497
-			$children[] = $this->createShareObject($data);
498
-		}
499
-		$cursor->closeCursor();
500
-
501
-		return $children;
502
-	}
503
-
504
-	/**
505
-	 * Delete a share (owner unShares the file)
506
-	 *
507
-	 * @param IShare $share
508
-	 */
509
-	public function delete(IShare $share) {
510
-
511
-		list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith());
512
-
513
-		$isOwner = false;
514
-
515
-		$this->removeShareFromTable($share);
516
-
517
-		// if the local user is the owner we can send the unShare request directly...
518
-		if ($this->userManager->userExists($share->getShareOwner())) {
519
-			$this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken());
520
-			$this->revokeShare($share, true);
521
-			$isOwner = true;
522
-		} else { // ... if not we need to correct ID for the unShare request
523
-			$remoteId = $this->getRemoteId($share);
524
-			$this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken());
525
-			$this->revokeShare($share, false);
526
-		}
527
-
528
-		// send revoke notification to the other user, if initiator and owner are not the same user
529
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
530
-			$remoteId = $this->getRemoteId($share);
531
-			if ($isOwner) {
532
-				list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
533
-			} else {
534
-				list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
535
-			}
536
-			$this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
537
-		}
538
-	}
539
-
540
-	/**
541
-	 * in case of a re-share we need to send the other use (initiator or owner)
542
-	 * a message that the file was unshared
543
-	 *
544
-	 * @param IShare $share
545
-	 * @param bool $isOwner the user can either be the owner or the user who re-sahred it
546
-	 * @throws ShareNotFound
547
-	 * @throws \OC\HintException
548
-	 */
549
-	protected function revokeShare($share, $isOwner) {
550
-		// also send a unShare request to the initiator, if this is a different user than the owner
551
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
552
-			if ($isOwner) {
553
-				list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
554
-			} else {
555
-				list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
556
-			}
557
-			$remoteId = $this->getRemoteId($share);
558
-			$this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
559
-		}
560
-	}
561
-
562
-	/**
563
-	 * remove share from table
564
-	 *
565
-	 * @param IShare $share
566
-	 */
567
-	public function removeShareFromTable(IShare $share) {
568
-		$this->removeShareFromTableById($share->getId());
569
-	}
570
-
571
-	/**
572
-	 * remove share from table
573
-	 *
574
-	 * @param string $shareId
575
-	 */
576
-	private function removeShareFromTableById($shareId) {
577
-		$qb = $this->dbConnection->getQueryBuilder();
578
-		$qb->delete('share')
579
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
580
-		$qb->execute();
581
-
582
-		$qb->delete('federated_reshares')
583
-			->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId)));
584
-		$qb->execute();
585
-	}
586
-
587
-	/**
588
-	 * @inheritdoc
589
-	 */
590
-	public function deleteFromSelf(IShare $share, $recipient) {
591
-		// nothing to do here. Technically deleteFromSelf in the context of federated
592
-		// shares is a umount of a external storage. This is handled here
593
-		// apps/files_sharing/lib/external/manager.php
594
-		// TODO move this code over to this app
595
-	}
596
-
597
-
598
-	public function getSharesInFolder($userId, Folder $node, $reshares) {
599
-		$qb = $this->dbConnection->getQueryBuilder();
600
-		$qb->select('*')
601
-			->from('share', 's')
602
-			->andWhere($qb->expr()->orX(
603
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
604
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
605
-			))
606
-			->andWhere(
607
-				$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))
608
-			);
609
-
610
-		/**
611
-		 * Reshares for this user are shares where they are the owner.
612
-		 */
613
-		if ($reshares === false) {
614
-			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
615
-		} else {
616
-			$qb->andWhere(
617
-				$qb->expr()->orX(
618
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
619
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
620
-				)
621
-			);
622
-		}
623
-
624
-		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
625
-		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
626
-
627
-		$qb->orderBy('id');
628
-
629
-		$cursor = $qb->execute();
630
-		$shares = [];
631
-		while ($data = $cursor->fetch()) {
632
-			$shares[$data['fileid']][] = $this->createShareObject($data);
633
-		}
634
-		$cursor->closeCursor();
635
-
636
-		return $shares;
637
-	}
638
-
639
-	/**
640
-	 * @inheritdoc
641
-	 */
642
-	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
643
-		$qb = $this->dbConnection->getQueryBuilder();
644
-		$qb->select('*')
645
-			->from('share');
646
-
647
-		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
648
-
649
-		/**
650
-		 * Reshares for this user are shares where they are the owner.
651
-		 */
652
-		if ($reshares === false) {
653
-			//Special case for old shares created via the web UI
654
-			$or1 = $qb->expr()->andX(
655
-				$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
656
-				$qb->expr()->isNull('uid_initiator')
657
-			);
658
-
659
-			$qb->andWhere(
660
-				$qb->expr()->orX(
661
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
662
-					$or1
663
-				)
664
-			);
665
-		} else {
666
-			$qb->andWhere(
667
-				$qb->expr()->orX(
668
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
669
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
670
-				)
671
-			);
672
-		}
673
-
674
-		if ($node !== null) {
675
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
676
-		}
677
-
678
-		if ($limit !== -1) {
679
-			$qb->setMaxResults($limit);
680
-		}
681
-
682
-		$qb->setFirstResult($offset);
683
-		$qb->orderBy('id');
684
-
685
-		$cursor = $qb->execute();
686
-		$shares = [];
687
-		while($data = $cursor->fetch()) {
688
-			$shares[] = $this->createShareObject($data);
689
-		}
690
-		$cursor->closeCursor();
691
-
692
-		return $shares;
693
-	}
694
-
695
-	/**
696
-	 * @inheritdoc
697
-	 */
698
-	public function getShareById($id, $recipientId = null) {
699
-		$qb = $this->dbConnection->getQueryBuilder();
700
-
701
-		$qb->select('*')
702
-			->from('share')
703
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
704
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
705
-
706
-		$cursor = $qb->execute();
707
-		$data = $cursor->fetch();
708
-		$cursor->closeCursor();
709
-
710
-		if ($data === false) {
711
-			throw new ShareNotFound();
712
-		}
713
-
714
-		try {
715
-			$share = $this->createShareObject($data);
716
-		} catch (InvalidShare $e) {
717
-			throw new ShareNotFound();
718
-		}
719
-
720
-		return $share;
721
-	}
722
-
723
-	/**
724
-	 * Get shares for a given path
725
-	 *
726
-	 * @param \OCP\Files\Node $path
727
-	 * @return IShare[]
728
-	 */
729
-	public function getSharesByPath(Node $path) {
730
-		$qb = $this->dbConnection->getQueryBuilder();
731
-
732
-		$cursor = $qb->select('*')
733
-			->from('share')
734
-			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
735
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
736
-			->execute();
737
-
738
-		$shares = [];
739
-		while($data = $cursor->fetch()) {
740
-			$shares[] = $this->createShareObject($data);
741
-		}
742
-		$cursor->closeCursor();
743
-
744
-		return $shares;
745
-	}
746
-
747
-	/**
748
-	 * @inheritdoc
749
-	 */
750
-	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
751
-		/** @var IShare[] $shares */
752
-		$shares = [];
753
-
754
-		//Get shares directly with this user
755
-		$qb = $this->dbConnection->getQueryBuilder();
756
-		$qb->select('*')
757
-			->from('share');
758
-
759
-		// Order by id
760
-		$qb->orderBy('id');
761
-
762
-		// Set limit and offset
763
-		if ($limit !== -1) {
764
-			$qb->setMaxResults($limit);
765
-		}
766
-		$qb->setFirstResult($offset);
767
-
768
-		$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
769
-		$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
770
-
771
-		// Filter by node if provided
772
-		if ($node !== null) {
773
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
774
-		}
775
-
776
-		$cursor = $qb->execute();
777
-
778
-		while($data = $cursor->fetch()) {
779
-			$shares[] = $this->createShareObject($data);
780
-		}
781
-		$cursor->closeCursor();
782
-
783
-
784
-		return $shares;
785
-	}
786
-
787
-	/**
788
-	 * Get a share by token
789
-	 *
790
-	 * @param string $token
791
-	 * @return IShare
792
-	 * @throws ShareNotFound
793
-	 */
794
-	public function getShareByToken($token) {
795
-		$qb = $this->dbConnection->getQueryBuilder();
796
-
797
-		$cursor = $qb->select('*')
798
-			->from('share')
799
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
800
-			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
801
-			->execute();
802
-
803
-		$data = $cursor->fetch();
804
-
805
-		if ($data === false) {
806
-			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
807
-		}
808
-
809
-		try {
810
-			$share = $this->createShareObject($data);
811
-		} catch (InvalidShare $e) {
812
-			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
813
-		}
814
-
815
-		return $share;
816
-	}
817
-
818
-	/**
819
-	 * get database row of a give share
820
-	 *
821
-	 * @param $id
822
-	 * @return array
823
-	 * @throws ShareNotFound
824
-	 */
825
-	private function getRawShare($id) {
826
-
827
-		// Now fetch the inserted share and create a complete share object
828
-		$qb = $this->dbConnection->getQueryBuilder();
829
-		$qb->select('*')
830
-			->from('share')
831
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
832
-
833
-		$cursor = $qb->execute();
834
-		$data = $cursor->fetch();
835
-		$cursor->closeCursor();
836
-
837
-		if ($data === false) {
838
-			throw new ShareNotFound;
839
-		}
840
-
841
-		return $data;
842
-	}
843
-
844
-	/**
845
-	 * Create a share object from an database row
846
-	 *
847
-	 * @param array $data
848
-	 * @return IShare
849
-	 * @throws InvalidShare
850
-	 * @throws ShareNotFound
851
-	 */
852
-	private function createShareObject($data) {
853
-
854
-		$share = new Share($this->rootFolder, $this->userManager);
855
-		$share->setId((int)$data['id'])
856
-			->setShareType((int)$data['share_type'])
857
-			->setPermissions((int)$data['permissions'])
858
-			->setTarget($data['file_target'])
859
-			->setMailSend((bool)$data['mail_send'])
860
-			->setToken($data['token']);
861
-
862
-		$shareTime = new \DateTime();
863
-		$shareTime->setTimestamp((int)$data['stime']);
864
-		$share->setShareTime($shareTime);
865
-		$share->setSharedWith($data['share_with']);
866
-
867
-		if ($data['uid_initiator'] !== null) {
868
-			$share->setShareOwner($data['uid_owner']);
869
-			$share->setSharedBy($data['uid_initiator']);
870
-		} else {
871
-			//OLD SHARE
872
-			$share->setSharedBy($data['uid_owner']);
873
-			$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
874
-
875
-			$owner = $path->getOwner();
876
-			$share->setShareOwner($owner->getUID());
877
-		}
878
-
879
-		$share->setNodeId((int)$data['file_source']);
880
-		$share->setNodeType($data['item_type']);
881
-
882
-		$share->setProviderId($this->identifier());
883
-
884
-		return $share;
885
-	}
886
-
887
-	/**
888
-	 * Get the node with file $id for $user
889
-	 *
890
-	 * @param string $userId
891
-	 * @param int $id
892
-	 * @return \OCP\Files\File|\OCP\Files\Folder
893
-	 * @throws InvalidShare
894
-	 */
895
-	private function getNode($userId, $id) {
896
-		try {
897
-			$userFolder = $this->rootFolder->getUserFolder($userId);
898
-		} catch (NotFoundException $e) {
899
-			throw new InvalidShare();
900
-		}
901
-
902
-		$nodes = $userFolder->getById($id);
903
-
904
-		if (empty($nodes)) {
905
-			throw new InvalidShare();
906
-		}
907
-
908
-		return $nodes[0];
909
-	}
910
-
911
-	/**
912
-	 * A user is deleted from the system
913
-	 * So clean up the relevant shares.
914
-	 *
915
-	 * @param string $uid
916
-	 * @param int $shareType
917
-	 */
918
-	public function userDeleted($uid, $shareType) {
919
-		//TODO: probabaly a good idea to send unshare info to remote servers
920
-
921
-		$qb = $this->dbConnection->getQueryBuilder();
922
-
923
-		$qb->delete('share')
924
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
925
-			->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
926
-			->execute();
927
-	}
928
-
929
-	/**
930
-	 * This provider does not handle groups
931
-	 *
932
-	 * @param string $gid
933
-	 */
934
-	public function groupDeleted($gid) {
935
-		// We don't handle groups here
936
-	}
937
-
938
-	/**
939
-	 * This provider does not handle groups
940
-	 *
941
-	 * @param string $uid
942
-	 * @param string $gid
943
-	 */
944
-	public function userDeletedFromGroup($uid, $gid) {
945
-		// We don't handle groups here
946
-	}
947
-
948
-	/**
949
-	 * check if users from other Nextcloud instances are allowed to mount public links share by this instance
950
-	 *
951
-	 * @return bool
952
-	 */
953
-	public function isOutgoingServer2serverShareEnabled() {
954
-		if ($this->gsConfig->onlyInternalFederation()) {
955
-			return false;
956
-		}
957
-		$result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes');
958
-		return ($result === 'yes');
959
-	}
960
-
961
-	/**
962
-	 * check if users are allowed to mount public links from other Nextclouds
963
-	 *
964
-	 * @return bool
965
-	 */
966
-	public function isIncomingServer2serverShareEnabled() {
967
-		if ($this->gsConfig->onlyInternalFederation()) {
968
-			return false;
969
-		}
970
-		$result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes');
971
-		return ($result === 'yes');
972
-	}
973
-
974
-	/**
975
-	 * Check if querying sharees on the lookup server is enabled
976
-	 *
977
-	 * @return bool
978
-	 */
979
-	public function isLookupServerQueriesEnabled() {
980
-		// in a global scale setup we should always query the lookup server
981
-		if ($this->gsConfig->isGlobalScaleEnabled()) {
982
-			return true;
983
-		}
984
-		$result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
985
-		return ($result === 'yes');
986
-	}
987
-
988
-
989
-	/**
990
-	 * Check if it is allowed to publish user specific data to the lookup server
991
-	 *
992
-	 * @return bool
993
-	 */
994
-	public function isLookupServerUploadEnabled() {
995
-		// in a global scale setup the admin is responsible to keep the lookup server up-to-date
996
-		if ($this->gsConfig->isGlobalScaleEnabled()) {
997
-			return false;
998
-		}
999
-		$result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes');
1000
-		return ($result === 'yes');
1001
-	}
1002
-
1003
-	/**
1004
-	 * @inheritdoc
1005
-	 */
1006
-	public function getAccessList($nodes, $currentAccess) {
1007
-		$ids = [];
1008
-		foreach ($nodes as $node) {
1009
-			$ids[] = $node->getId();
1010
-		}
1011
-
1012
-		$qb = $this->dbConnection->getQueryBuilder();
1013
-		$qb->select('share_with', 'token', 'file_source')
1014
-			->from('share')
1015
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
1016
-			->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1017
-			->andWhere($qb->expr()->orX(
1018
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1019
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1020
-			));
1021
-		$cursor = $qb->execute();
1022
-
1023
-		if ($currentAccess === false) {
1024
-			$remote = $cursor->fetch() !== false;
1025
-			$cursor->closeCursor();
1026
-
1027
-			return ['remote' => $remote];
1028
-		}
1029
-
1030
-		$remote = [];
1031
-		while ($row = $cursor->fetch()) {
1032
-			$remote[$row['share_with']] = [
1033
-				'node_id' => $row['file_source'],
1034
-				'token' => $row['token'],
1035
-			];
1036
-		}
1037
-		$cursor->closeCursor();
1038
-
1039
-		return ['remote' => $remote];
1040
-	}
476
+        return $share;
477
+    }
478
+
479
+    /**
480
+     * Get all children of this share
481
+     *
482
+     * @param IShare $parent
483
+     * @return IShare[]
484
+     */
485
+    public function getChildren(IShare $parent) {
486
+        $children = [];
487
+
488
+        $qb = $this->dbConnection->getQueryBuilder();
489
+        $qb->select('*')
490
+            ->from('share')
491
+            ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
492
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
493
+            ->orderBy('id');
494
+
495
+        $cursor = $qb->execute();
496
+        while($data = $cursor->fetch()) {
497
+            $children[] = $this->createShareObject($data);
498
+        }
499
+        $cursor->closeCursor();
500
+
501
+        return $children;
502
+    }
503
+
504
+    /**
505
+     * Delete a share (owner unShares the file)
506
+     *
507
+     * @param IShare $share
508
+     */
509
+    public function delete(IShare $share) {
510
+
511
+        list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedWith());
512
+
513
+        $isOwner = false;
514
+
515
+        $this->removeShareFromTable($share);
516
+
517
+        // if the local user is the owner we can send the unShare request directly...
518
+        if ($this->userManager->userExists($share->getShareOwner())) {
519
+            $this->notifications->sendRemoteUnShare($remote, $share->getId(), $share->getToken());
520
+            $this->revokeShare($share, true);
521
+            $isOwner = true;
522
+        } else { // ... if not we need to correct ID for the unShare request
523
+            $remoteId = $this->getRemoteId($share);
524
+            $this->notifications->sendRemoteUnShare($remote, $remoteId, $share->getToken());
525
+            $this->revokeShare($share, false);
526
+        }
527
+
528
+        // send revoke notification to the other user, if initiator and owner are not the same user
529
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
530
+            $remoteId = $this->getRemoteId($share);
531
+            if ($isOwner) {
532
+                list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
533
+            } else {
534
+                list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
535
+            }
536
+            $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
537
+        }
538
+    }
539
+
540
+    /**
541
+     * in case of a re-share we need to send the other use (initiator or owner)
542
+     * a message that the file was unshared
543
+     *
544
+     * @param IShare $share
545
+     * @param bool $isOwner the user can either be the owner or the user who re-sahred it
546
+     * @throws ShareNotFound
547
+     * @throws \OC\HintException
548
+     */
549
+    protected function revokeShare($share, $isOwner) {
550
+        // also send a unShare request to the initiator, if this is a different user than the owner
551
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
552
+            if ($isOwner) {
553
+                list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
554
+            } else {
555
+                list(, $remote) = $this->addressHandler->splitUserRemote($share->getShareOwner());
556
+            }
557
+            $remoteId = $this->getRemoteId($share);
558
+            $this->notifications->sendRevokeShare($remote, $remoteId, $share->getToken());
559
+        }
560
+    }
561
+
562
+    /**
563
+     * remove share from table
564
+     *
565
+     * @param IShare $share
566
+     */
567
+    public function removeShareFromTable(IShare $share) {
568
+        $this->removeShareFromTableById($share->getId());
569
+    }
570
+
571
+    /**
572
+     * remove share from table
573
+     *
574
+     * @param string $shareId
575
+     */
576
+    private function removeShareFromTableById($shareId) {
577
+        $qb = $this->dbConnection->getQueryBuilder();
578
+        $qb->delete('share')
579
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
580
+        $qb->execute();
581
+
582
+        $qb->delete('federated_reshares')
583
+            ->where($qb->expr()->eq('share_id', $qb->createNamedParameter($shareId)));
584
+        $qb->execute();
585
+    }
586
+
587
+    /**
588
+     * @inheritdoc
589
+     */
590
+    public function deleteFromSelf(IShare $share, $recipient) {
591
+        // nothing to do here. Technically deleteFromSelf in the context of federated
592
+        // shares is a umount of a external storage. This is handled here
593
+        // apps/files_sharing/lib/external/manager.php
594
+        // TODO move this code over to this app
595
+    }
596
+
597
+
598
+    public function getSharesInFolder($userId, Folder $node, $reshares) {
599
+        $qb = $this->dbConnection->getQueryBuilder();
600
+        $qb->select('*')
601
+            ->from('share', 's')
602
+            ->andWhere($qb->expr()->orX(
603
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
604
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
605
+            ))
606
+            ->andWhere(
607
+                $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE))
608
+            );
609
+
610
+        /**
611
+         * Reshares for this user are shares where they are the owner.
612
+         */
613
+        if ($reshares === false) {
614
+            $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
615
+        } else {
616
+            $qb->andWhere(
617
+                $qb->expr()->orX(
618
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
619
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
620
+                )
621
+            );
622
+        }
623
+
624
+        $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
625
+        $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
626
+
627
+        $qb->orderBy('id');
628
+
629
+        $cursor = $qb->execute();
630
+        $shares = [];
631
+        while ($data = $cursor->fetch()) {
632
+            $shares[$data['fileid']][] = $this->createShareObject($data);
633
+        }
634
+        $cursor->closeCursor();
635
+
636
+        return $shares;
637
+    }
638
+
639
+    /**
640
+     * @inheritdoc
641
+     */
642
+    public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
643
+        $qb = $this->dbConnection->getQueryBuilder();
644
+        $qb->select('*')
645
+            ->from('share');
646
+
647
+        $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
648
+
649
+        /**
650
+         * Reshares for this user are shares where they are the owner.
651
+         */
652
+        if ($reshares === false) {
653
+            //Special case for old shares created via the web UI
654
+            $or1 = $qb->expr()->andX(
655
+                $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
656
+                $qb->expr()->isNull('uid_initiator')
657
+            );
658
+
659
+            $qb->andWhere(
660
+                $qb->expr()->orX(
661
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
662
+                    $or1
663
+                )
664
+            );
665
+        } else {
666
+            $qb->andWhere(
667
+                $qb->expr()->orX(
668
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
669
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
670
+                )
671
+            );
672
+        }
673
+
674
+        if ($node !== null) {
675
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
676
+        }
677
+
678
+        if ($limit !== -1) {
679
+            $qb->setMaxResults($limit);
680
+        }
681
+
682
+        $qb->setFirstResult($offset);
683
+        $qb->orderBy('id');
684
+
685
+        $cursor = $qb->execute();
686
+        $shares = [];
687
+        while($data = $cursor->fetch()) {
688
+            $shares[] = $this->createShareObject($data);
689
+        }
690
+        $cursor->closeCursor();
691
+
692
+        return $shares;
693
+    }
694
+
695
+    /**
696
+     * @inheritdoc
697
+     */
698
+    public function getShareById($id, $recipientId = null) {
699
+        $qb = $this->dbConnection->getQueryBuilder();
700
+
701
+        $qb->select('*')
702
+            ->from('share')
703
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
704
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
705
+
706
+        $cursor = $qb->execute();
707
+        $data = $cursor->fetch();
708
+        $cursor->closeCursor();
709
+
710
+        if ($data === false) {
711
+            throw new ShareNotFound();
712
+        }
713
+
714
+        try {
715
+            $share = $this->createShareObject($data);
716
+        } catch (InvalidShare $e) {
717
+            throw new ShareNotFound();
718
+        }
719
+
720
+        return $share;
721
+    }
722
+
723
+    /**
724
+     * Get shares for a given path
725
+     *
726
+     * @param \OCP\Files\Node $path
727
+     * @return IShare[]
728
+     */
729
+    public function getSharesByPath(Node $path) {
730
+        $qb = $this->dbConnection->getQueryBuilder();
731
+
732
+        $cursor = $qb->select('*')
733
+            ->from('share')
734
+            ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
735
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
736
+            ->execute();
737
+
738
+        $shares = [];
739
+        while($data = $cursor->fetch()) {
740
+            $shares[] = $this->createShareObject($data);
741
+        }
742
+        $cursor->closeCursor();
743
+
744
+        return $shares;
745
+    }
746
+
747
+    /**
748
+     * @inheritdoc
749
+     */
750
+    public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
751
+        /** @var IShare[] $shares */
752
+        $shares = [];
753
+
754
+        //Get shares directly with this user
755
+        $qb = $this->dbConnection->getQueryBuilder();
756
+        $qb->select('*')
757
+            ->from('share');
758
+
759
+        // Order by id
760
+        $qb->orderBy('id');
761
+
762
+        // Set limit and offset
763
+        if ($limit !== -1) {
764
+            $qb->setMaxResults($limit);
765
+        }
766
+        $qb->setFirstResult($offset);
767
+
768
+        $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)));
769
+        $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
770
+
771
+        // Filter by node if provided
772
+        if ($node !== null) {
773
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
774
+        }
775
+
776
+        $cursor = $qb->execute();
777
+
778
+        while($data = $cursor->fetch()) {
779
+            $shares[] = $this->createShareObject($data);
780
+        }
781
+        $cursor->closeCursor();
782
+
783
+
784
+        return $shares;
785
+    }
786
+
787
+    /**
788
+     * Get a share by token
789
+     *
790
+     * @param string $token
791
+     * @return IShare
792
+     * @throws ShareNotFound
793
+     */
794
+    public function getShareByToken($token) {
795
+        $qb = $this->dbConnection->getQueryBuilder();
796
+
797
+        $cursor = $qb->select('*')
798
+            ->from('share')
799
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_REMOTE)))
800
+            ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
801
+            ->execute();
802
+
803
+        $data = $cursor->fetch();
804
+
805
+        if ($data === false) {
806
+            throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
807
+        }
808
+
809
+        try {
810
+            $share = $this->createShareObject($data);
811
+        } catch (InvalidShare $e) {
812
+            throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
813
+        }
814
+
815
+        return $share;
816
+    }
817
+
818
+    /**
819
+     * get database row of a give share
820
+     *
821
+     * @param $id
822
+     * @return array
823
+     * @throws ShareNotFound
824
+     */
825
+    private function getRawShare($id) {
826
+
827
+        // Now fetch the inserted share and create a complete share object
828
+        $qb = $this->dbConnection->getQueryBuilder();
829
+        $qb->select('*')
830
+            ->from('share')
831
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
832
+
833
+        $cursor = $qb->execute();
834
+        $data = $cursor->fetch();
835
+        $cursor->closeCursor();
836
+
837
+        if ($data === false) {
838
+            throw new ShareNotFound;
839
+        }
840
+
841
+        return $data;
842
+    }
843
+
844
+    /**
845
+     * Create a share object from an database row
846
+     *
847
+     * @param array $data
848
+     * @return IShare
849
+     * @throws InvalidShare
850
+     * @throws ShareNotFound
851
+     */
852
+    private function createShareObject($data) {
853
+
854
+        $share = new Share($this->rootFolder, $this->userManager);
855
+        $share->setId((int)$data['id'])
856
+            ->setShareType((int)$data['share_type'])
857
+            ->setPermissions((int)$data['permissions'])
858
+            ->setTarget($data['file_target'])
859
+            ->setMailSend((bool)$data['mail_send'])
860
+            ->setToken($data['token']);
861
+
862
+        $shareTime = new \DateTime();
863
+        $shareTime->setTimestamp((int)$data['stime']);
864
+        $share->setShareTime($shareTime);
865
+        $share->setSharedWith($data['share_with']);
866
+
867
+        if ($data['uid_initiator'] !== null) {
868
+            $share->setShareOwner($data['uid_owner']);
869
+            $share->setSharedBy($data['uid_initiator']);
870
+        } else {
871
+            //OLD SHARE
872
+            $share->setSharedBy($data['uid_owner']);
873
+            $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
874
+
875
+            $owner = $path->getOwner();
876
+            $share->setShareOwner($owner->getUID());
877
+        }
878
+
879
+        $share->setNodeId((int)$data['file_source']);
880
+        $share->setNodeType($data['item_type']);
881
+
882
+        $share->setProviderId($this->identifier());
883
+
884
+        return $share;
885
+    }
886
+
887
+    /**
888
+     * Get the node with file $id for $user
889
+     *
890
+     * @param string $userId
891
+     * @param int $id
892
+     * @return \OCP\Files\File|\OCP\Files\Folder
893
+     * @throws InvalidShare
894
+     */
895
+    private function getNode($userId, $id) {
896
+        try {
897
+            $userFolder = $this->rootFolder->getUserFolder($userId);
898
+        } catch (NotFoundException $e) {
899
+            throw new InvalidShare();
900
+        }
901
+
902
+        $nodes = $userFolder->getById($id);
903
+
904
+        if (empty($nodes)) {
905
+            throw new InvalidShare();
906
+        }
907
+
908
+        return $nodes[0];
909
+    }
910
+
911
+    /**
912
+     * A user is deleted from the system
913
+     * So clean up the relevant shares.
914
+     *
915
+     * @param string $uid
916
+     * @param int $shareType
917
+     */
918
+    public function userDeleted($uid, $shareType) {
919
+        //TODO: probabaly a good idea to send unshare info to remote servers
920
+
921
+        $qb = $this->dbConnection->getQueryBuilder();
922
+
923
+        $qb->delete('share')
924
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
925
+            ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
926
+            ->execute();
927
+    }
928
+
929
+    /**
930
+     * This provider does not handle groups
931
+     *
932
+     * @param string $gid
933
+     */
934
+    public function groupDeleted($gid) {
935
+        // We don't handle groups here
936
+    }
937
+
938
+    /**
939
+     * This provider does not handle groups
940
+     *
941
+     * @param string $uid
942
+     * @param string $gid
943
+     */
944
+    public function userDeletedFromGroup($uid, $gid) {
945
+        // We don't handle groups here
946
+    }
947
+
948
+    /**
949
+     * check if users from other Nextcloud instances are allowed to mount public links share by this instance
950
+     *
951
+     * @return bool
952
+     */
953
+    public function isOutgoingServer2serverShareEnabled() {
954
+        if ($this->gsConfig->onlyInternalFederation()) {
955
+            return false;
956
+        }
957
+        $result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes');
958
+        return ($result === 'yes');
959
+    }
960
+
961
+    /**
962
+     * check if users are allowed to mount public links from other Nextclouds
963
+     *
964
+     * @return bool
965
+     */
966
+    public function isIncomingServer2serverShareEnabled() {
967
+        if ($this->gsConfig->onlyInternalFederation()) {
968
+            return false;
969
+        }
970
+        $result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes');
971
+        return ($result === 'yes');
972
+    }
973
+
974
+    /**
975
+     * Check if querying sharees on the lookup server is enabled
976
+     *
977
+     * @return bool
978
+     */
979
+    public function isLookupServerQueriesEnabled() {
980
+        // in a global scale setup we should always query the lookup server
981
+        if ($this->gsConfig->isGlobalScaleEnabled()) {
982
+            return true;
983
+        }
984
+        $result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'no');
985
+        return ($result === 'yes');
986
+    }
987
+
988
+
989
+    /**
990
+     * Check if it is allowed to publish user specific data to the lookup server
991
+     *
992
+     * @return bool
993
+     */
994
+    public function isLookupServerUploadEnabled() {
995
+        // in a global scale setup the admin is responsible to keep the lookup server up-to-date
996
+        if ($this->gsConfig->isGlobalScaleEnabled()) {
997
+            return false;
998
+        }
999
+        $result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes');
1000
+        return ($result === 'yes');
1001
+    }
1002
+
1003
+    /**
1004
+     * @inheritdoc
1005
+     */
1006
+    public function getAccessList($nodes, $currentAccess) {
1007
+        $ids = [];
1008
+        foreach ($nodes as $node) {
1009
+            $ids[] = $node->getId();
1010
+        }
1011
+
1012
+        $qb = $this->dbConnection->getQueryBuilder();
1013
+        $qb->select('share_with', 'token', 'file_source')
1014
+            ->from('share')
1015
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_REMOTE)))
1016
+            ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1017
+            ->andWhere($qb->expr()->orX(
1018
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1019
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1020
+            ));
1021
+        $cursor = $qb->execute();
1022
+
1023
+        if ($currentAccess === false) {
1024
+            $remote = $cursor->fetch() !== false;
1025
+            $cursor->closeCursor();
1026
+
1027
+            return ['remote' => $remote];
1028
+        }
1029
+
1030
+        $remote = [];
1031
+        while ($row = $cursor->fetch()) {
1032
+            $remote[$row['share_with']] = [
1033
+                'node_id' => $row['file_source'],
1034
+                'token' => $row['token'],
1035
+            ];
1036
+        }
1037
+        $cursor->closeCursor();
1038
+
1039
+        return ['remote' => $remote];
1040
+    }
1041 1041
 }
Please login to merge, or discard this patch.
apps/federatedfilesharing/lib/Controller/RequestHandlerController.php 1 patch
Indentation   +635 added lines, -635 removed lines patch added patch discarded remove patch
@@ -51,639 +51,639 @@
 block discarded – undo
51 51
 
52 52
 class RequestHandlerController extends OCSController {
53 53
 
54
-	/** @var FederatedShareProvider */
55
-	private $federatedShareProvider;
56
-
57
-	/** @var IDBConnection */
58
-	private $connection;
59
-
60
-	/** @var Share\IManager */
61
-	private $shareManager;
62
-
63
-	/** @var Notifications */
64
-	private $notifications;
65
-
66
-	/** @var AddressHandler */
67
-	private $addressHandler;
68
-
69
-	/** @var  IUserManager */
70
-	private $userManager;
71
-
72
-	/** @var string */
73
-	private $shareTable = 'share';
74
-
75
-	/** @var ICloudIdManager */
76
-	private $cloudIdManager;
77
-
78
-	/** @var ILogger */
79
-	private $logger;
80
-
81
-	/**
82
-	 * Server2Server constructor.
83
-	 *
84
-	 * @param string $appName
85
-	 * @param IRequest $request
86
-	 * @param FederatedShareProvider $federatedShareProvider
87
-	 * @param IDBConnection $connection
88
-	 * @param Share\IManager $shareManager
89
-	 * @param Notifications $notifications
90
-	 * @param AddressHandler $addressHandler
91
-	 * @param IUserManager $userManager
92
-	 * @param ICloudIdManager $cloudIdManager
93
-	 */
94
-	public function __construct($appName,
95
-								IRequest $request,
96
-								FederatedShareProvider $federatedShareProvider,
97
-								IDBConnection $connection,
98
-								Share\IManager $shareManager,
99
-								Notifications $notifications,
100
-								AddressHandler $addressHandler,
101
-								IUserManager $userManager,
102
-								ICloudIdManager $cloudIdManager,
103
-								ILogger $logger
104
-	) {
105
-		parent::__construct($appName, $request);
106
-
107
-		$this->federatedShareProvider = $federatedShareProvider;
108
-		$this->connection = $connection;
109
-		$this->shareManager = $shareManager;
110
-		$this->notifications = $notifications;
111
-		$this->addressHandler = $addressHandler;
112
-		$this->userManager = $userManager;
113
-		$this->cloudIdManager = $cloudIdManager;
114
-		$this->logger = $logger;
115
-	}
116
-
117
-	/**
118
-	 * @NoCSRFRequired
119
-	 * @PublicPage
120
-	 *
121
-	 * create a new share
122
-	 *
123
-	 * @return Http\DataResponse
124
-	 * @throws OCSException
125
-	 */
126
-	public function createShare() {
127
-
128
-		if (!$this->isS2SEnabled(true)) {
129
-			throw new OCSException('Server does not support federated cloud sharing', 503);
130
-		}
131
-
132
-		$remote = isset($_POST['remote']) ? $_POST['remote'] : null;
133
-		$token = isset($_POST['token']) ? $_POST['token'] : null;
134
-		$name = isset($_POST['name']) ? $_POST['name'] : null;
135
-		$owner = isset($_POST['owner']) ? $_POST['owner'] : null;
136
-		$sharedBy = isset($_POST['sharedBy']) ? $_POST['sharedBy'] : null;
137
-		$shareWith = isset($_POST['shareWith']) ? $_POST['shareWith'] : null;
138
-		$remoteId = isset($_POST['remoteId']) ? (int)$_POST['remoteId'] : null;
139
-		$sharedByFederatedId = isset($_POST['sharedByFederatedId']) ? $_POST['sharedByFederatedId'] : null;
140
-		$ownerFederatedId = isset($_POST['ownerFederatedId']) ? $_POST['ownerFederatedId'] : null;
141
-
142
-		if ($remote && $token && $name && $owner && $remoteId && $shareWith) {
143
-
144
-			if (!\OCP\Util::isValidFileName($name)) {
145
-				throw new OCSException('The mountpoint name contains invalid characters.', 400);
146
-			}
147
-
148
-			// FIXME this should be a method in the user management instead
149
-			$this->logger->debug('shareWith before, ' . $shareWith, ['app' => 'files_sharing']);
150
-			\OCP\Util::emitHook(
151
-				'\OCA\Files_Sharing\API\Server2Server',
152
-				'preLoginNameUsedAsUserName',
153
-				array('uid' => &$shareWith)
154
-			);
155
-			$this->logger->debug('shareWith after, ' . $shareWith, ['app' => 'files_sharing']);
156
-
157
-			if (!\OC::$server->getUserManager()->userExists($shareWith)) {
158
-				throw new OCSException('User does not exists', 400);
159
-			}
160
-
161
-			\OC_Util::setupFS($shareWith);
162
-
163
-			$externalManager = new \OCA\Files_Sharing\External\Manager(
164
-					\OC::$server->getDatabaseConnection(),
165
-					\OC\Files\Filesystem::getMountManager(),
166
-					\OC\Files\Filesystem::getLoader(),
167
-					\OC::$server->getHTTPClientService(),
168
-					\OC::$server->getNotificationManager(),
169
-					\OC::$server->query(\OCP\OCS\IDiscoveryService::class),
170
-					$shareWith
171
-				);
172
-
173
-			try {
174
-				$externalManager->addShare($remote, $token, '', $name, $owner, false, $shareWith, $remoteId);
175
-				$shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external');
176
-
177
-				if ($ownerFederatedId === null) {
178
-					$ownerFederatedId = $this->cloudIdManager->getCloudId($owner, $this->cleanupRemote($remote))->getId();
179
-				}
180
-				// if the owner of the share and the initiator are the same user
181
-				// we also complete the federated share ID for the initiator
182
-				if ($sharedByFederatedId === null && $owner === $sharedBy) {
183
-					$sharedByFederatedId = $ownerFederatedId;
184
-				}
185
-
186
-				$event = \OC::$server->getActivityManager()->generateEvent();
187
-				$event->setApp('files_sharing')
188
-					->setType('remote_share')
189
-					->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/')])
190
-					->setAffectedUser($shareWith)
191
-					->setObject('remote_share', (int)$shareId, $name);
192
-				\OC::$server->getActivityManager()->publish($event);
193
-
194
-				$urlGenerator = \OC::$server->getURLGenerator();
195
-
196
-				$notificationManager = \OC::$server->getNotificationManager();
197
-				$notification = $notificationManager->createNotification();
198
-				$notification->setApp('files_sharing')
199
-					->setUser($shareWith)
200
-					->setDateTime(new \DateTime())
201
-					->setObject('remote_share', $shareId)
202
-					->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/')]);
203
-
204
-				$declineAction = $notification->createAction();
205
-				$declineAction->setLabel('decline')
206
-					->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE');
207
-				$notification->addAction($declineAction);
208
-
209
-				$acceptAction = $notification->createAction();
210
-				$acceptAction->setLabel('accept')
211
-					->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST');
212
-				$notification->addAction($acceptAction);
213
-
214
-				$notificationManager->notify($notification);
215
-
216
-				return new Http\DataResponse();
217
-			} catch (\Exception $e) {
218
-				$this->logger->logException($e, [
219
-					'message' => 'Server can not add remote share.',
220
-					'level' => ILogger::ERROR,
221
-					'app' => 'files_sharing'
222
-				]);
223
-				throw new OCSException('internal server error, was not able to add share from ' . $remote, 500);
224
-			}
225
-		}
226
-
227
-		throw new OCSException('server can not add remote share, missing parameter', 400);
228
-	}
229
-
230
-	/**
231
-	 * @NoCSRFRequired
232
-	 * @PublicPage
233
-	 *
234
-	 * create re-share on behalf of another user
235
-	 *
236
-	 * @param int $id
237
-	 * @return Http\DataResponse
238
-	 * @throws OCSBadRequestException
239
-	 * @throws OCSForbiddenException
240
-	 * @throws OCSNotFoundException
241
-	 */
242
-	public function reShare($id) {
243
-
244
-		$token = $this->request->getParam('token', null);
245
-		$shareWith = $this->request->getParam('shareWith', null);
246
-		$permission = (int)$this->request->getParam('permission', null);
247
-		$remoteId = (int)$this->request->getParam('remoteId', null);
248
-
249
-		if ($id === null ||
250
-			$token === null ||
251
-			$shareWith === null ||
252
-			$permission === null ||
253
-			$remoteId === null
254
-		) {
255
-			throw new OCSBadRequestException();
256
-		}
257
-
258
-		try {
259
-			$share = $this->federatedShareProvider->getShareById($id);
260
-		} catch (Share\Exceptions\ShareNotFound $e) {
261
-			throw new OCSNotFoundException();
262
-		}
263
-
264
-		// don't allow to share a file back to the owner
265
-		list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith);
266
-		$owner = $share->getShareOwner();
267
-		$currentServer = $this->addressHandler->generateRemoteURL();
268
-		if ($this->addressHandler->compareAddresses($user, $remote, $owner, $currentServer)) {
269
-			throw new OCSForbiddenException();
270
-		}
271
-
272
-		if ($this->verifyShare($share, $token)) {
273
-
274
-			// check if re-sharing is allowed
275
-			if ($share->getPermissions() | ~Constants::PERMISSION_SHARE) {
276
-				$share->setPermissions($share->getPermissions() & $permission);
277
-				// the recipient of the initial share is now the initiator for the re-share
278
-				$share->setSharedBy($share->getSharedWith());
279
-				$share->setSharedWith($shareWith);
280
-				try {
281
-					$result = $this->federatedShareProvider->create($share);
282
-					$this->federatedShareProvider->storeRemoteId((int)$result->getId(), $remoteId);
283
-					return new Http\DataResponse([
284
-						'token' => $result->getToken(),
285
-						'remoteId' => $result->getId()
286
-					]);
287
-				} catch (\Exception $e) {
288
-					throw new OCSBadRequestException();
289
-				}
290
-			} else {
291
-				throw new OCSForbiddenException();
292
-			}
293
-		}
294
-		throw new OCSBadRequestException();
295
-	}
296
-
297
-	/**
298
-	 * @NoCSRFRequired
299
-	 * @PublicPage
300
-	 *
301
-	 * accept server-to-server share
302
-	 *
303
-	 * @param int $id
304
-	 * @return Http\DataResponse
305
-	 * @throws OCSException
306
-	 */
307
-	public function acceptShare($id) {
308
-
309
-		if (!$this->isS2SEnabled()) {
310
-			throw new OCSException('Server does not support federated cloud sharing', 503);
311
-		}
312
-
313
-		$token = isset($_POST['token']) ? $_POST['token'] : null;
314
-
315
-		try {
316
-			$share = $this->federatedShareProvider->getShareById($id);
317
-		} catch (Share\Exceptions\ShareNotFound $e) {
318
-			return new Http\DataResponse();
319
-		}
320
-
321
-		if ($this->verifyShare($share, $token)) {
322
-			$this->executeAcceptShare($share);
323
-			if ($share->getShareOwner() !== $share->getSharedBy()) {
324
-				list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
325
-				$remoteId = $this->federatedShareProvider->getRemoteId($share);
326
-				$this->notifications->sendAcceptShare($remote, $remoteId, $share->getToken());
327
-			}
328
-		}
329
-
330
-		return new Http\DataResponse();
331
-	}
332
-
333
-	protected function executeAcceptShare(Share\IShare $share) {
334
-		$fileId = (int) $share->getNode()->getId();
335
-		list($file, $link) = $this->getFile($this->getCorrectUid($share), $fileId);
336
-
337
-		$event = \OC::$server->getActivityManager()->generateEvent();
338
-		$event->setApp('files_sharing')
339
-			->setType('remote_share')
340
-			->setAffectedUser($this->getCorrectUid($share))
341
-			->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share->getSharedWith(), [$fileId => $file]])
342
-			->setObject('files', $fileId, $file)
343
-			->setLink($link);
344
-		\OC::$server->getActivityManager()->publish($event);
345
-	}
346
-
347
-	/**
348
-	 * @NoCSRFRequired
349
-	 * @PublicPage
350
-	 *
351
-	 * decline server-to-server share
352
-	 *
353
-	 * @param int $id
354
-	 * @return Http\DataResponse
355
-	 * @throws OCSException
356
-	 */
357
-	public function declineShare($id) {
358
-
359
-		if (!$this->isS2SEnabled()) {
360
-			throw new OCSException('Server does not support federated cloud sharing', 503);
361
-		}
362
-
363
-		$token = isset($_POST['token']) ? $_POST['token'] : null;
364
-
365
-		try {
366
-			$share = $this->federatedShareProvider->getShareById($id);
367
-		} catch (Share\Exceptions\ShareNotFound $e) {
368
-			return new Http\DataResponse();
369
-		}
370
-
371
-		if ($this->verifyShare($share, $token)) {
372
-			if ($share->getShareOwner() !== $share->getSharedBy()) {
373
-				list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
374
-				$remoteId = $this->federatedShareProvider->getRemoteId($share);
375
-				$this->notifications->sendDeclineShare($remote, $remoteId, $share->getToken());
376
-			}
377
-			$this->executeDeclineShare($share);
378
-		}
379
-
380
-		return new Http\DataResponse();
381
-	}
382
-
383
-	/**
384
-	 * delete declined share and create a activity
385
-	 *
386
-	 * @param Share\IShare $share
387
-	 */
388
-	protected function executeDeclineShare(Share\IShare $share) {
389
-		$this->federatedShareProvider->removeShareFromTable($share);
390
-		$fileId = (int) $share->getNode()->getId();
391
-		list($file, $link) = $this->getFile($this->getCorrectUid($share), $fileId);
392
-
393
-		$event = \OC::$server->getActivityManager()->generateEvent();
394
-		$event->setApp('files_sharing')
395
-			->setType('remote_share')
396
-			->setAffectedUser($this->getCorrectUid($share))
397
-			->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_DECLINED, [$share->getSharedWith(), [$fileId => $file]])
398
-			->setObject('files', $fileId, $file)
399
-			->setLink($link);
400
-		\OC::$server->getActivityManager()->publish($event);
401
-
402
-	}
403
-
404
-	/**
405
-	 * check if we are the initiator or the owner of a re-share and return the correct UID
406
-	 *
407
-	 * @param Share\IShare $share
408
-	 * @return string
409
-	 */
410
-	protected function getCorrectUid(Share\IShare $share) {
411
-		if ($this->userManager->userExists($share->getShareOwner())) {
412
-			return $share->getShareOwner();
413
-		}
414
-
415
-		return $share->getSharedBy();
416
-	}
417
-
418
-	/**
419
-	 * @NoCSRFRequired
420
-	 * @PublicPage
421
-	 *
422
-	 * remove server-to-server share if it was unshared by the owner
423
-	 *
424
-	 * @param int $id
425
-	 * @return Http\DataResponse
426
-	 * @throws OCSException
427
-	 */
428
-	public function unshare($id) {
429
-
430
-		if (!$this->isS2SEnabled()) {
431
-			throw new OCSException('Server does not support federated cloud sharing', 503);
432
-		}
433
-
434
-		$token = isset($_POST['token']) ? $_POST['token'] : null;
435
-
436
-		$qb = $this->connection->getQueryBuilder();
437
-		$qb->select('*')
438
-			->from('share_external')
439
-			->where(
440
-				$qb->expr()->andX(
441
-					$qb->expr()->eq('remote_id', $qb->createNamedParameter($id)),
442
-					$qb->expr()->eq('share_token', $qb->createNamedParameter($token))
443
-				)
444
-			);
445
-
446
-		$result = $qb->execute();
447
-		$share = $result->fetch();
448
-		$result->closeCursor();
449
-
450
-		if ($token && $id && !empty($share)) {
451
-
452
-			$remote = $this->cleanupRemote($share['remote']);
453
-
454
-			$owner = $this->cloudIdManager->getCloudId($share['owner'], $remote);
455
-			$mountpoint = $share['mountpoint'];
456
-			$user = $share['user'];
457
-
458
-			$qb = $this->connection->getQueryBuilder();
459
-			$qb->delete('share_external')
460
-				->where(
461
-					$qb->expr()->andX(
462
-						$qb->expr()->eq('remote_id', $qb->createNamedParameter($id)),
463
-						$qb->expr()->eq('share_token', $qb->createNamedParameter($token))
464
-					)
465
-				);
466
-
467
-			$result = $qb->execute();
468
-			$result->closeCursor();
469
-
470
-			if ($share['accepted']) {
471
-				$path = trim($mountpoint, '/');
472
-			} else {
473
-				$path = trim($share['name'], '/');
474
-			}
475
-
476
-			$notificationManager = \OC::$server->getNotificationManager();
477
-			$notification = $notificationManager->createNotification();
478
-			$notification->setApp('files_sharing')
479
-				->setUser($share['user'])
480
-				->setObject('remote_share', (int)$share['id']);
481
-			$notificationManager->markProcessed($notification);
482
-
483
-			$event = \OC::$server->getActivityManager()->generateEvent();
484
-			$event->setApp('files_sharing')
485
-				->setType('remote_share')
486
-				->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path])
487
-				->setAffectedUser($user)
488
-				->setObject('remote_share', (int)$share['id'], $path);
489
-			\OC::$server->getActivityManager()->publish($event);
490
-		}
491
-
492
-		return new Http\DataResponse();
493
-	}
494
-
495
-	private function cleanupRemote($remote) {
496
-		$remote = substr($remote, strpos($remote, '://') + 3);
497
-
498
-		return rtrim($remote, '/');
499
-	}
500
-
501
-
502
-	/**
503
-	 * @NoCSRFRequired
504
-	 * @PublicPage
505
-	 *
506
-	 * federated share was revoked, either by the owner or the re-sharer
507
-	 *
508
-	 * @param int $id
509
-	 * @return Http\DataResponse
510
-	 * @throws OCSBadRequestException
511
-	 */
512
-	public function revoke($id) {
513
-		$token = $this->request->getParam('token');
514
-
515
-		$share = $this->federatedShareProvider->getShareById($id);
516
-
517
-		if ($this->verifyShare($share, $token)) {
518
-			$this->federatedShareProvider->removeShareFromTable($share);
519
-			return new Http\DataResponse();
520
-		}
521
-
522
-		throw new OCSBadRequestException();
523
-	}
524
-
525
-	/**
526
-	 * get share
527
-	 *
528
-	 * @param int $id
529
-	 * @param string $token
530
-	 * @return array|bool
531
-	 */
532
-	protected function getShare($id, $token) {
533
-		$query = $this->connection->getQueryBuilder();
534
-		$query->select('*')->from($this->shareTable)
535
-			->where($query->expr()->eq('token', $query->createNamedParameter($token)))
536
-			->andWhere($query->expr()->eq('share_type', $query->createNamedParameter(FederatedShareProvider::SHARE_TYPE_REMOTE)))
537
-			->andWhere($query->expr()->eq('id', $query->createNamedParameter($id)));
538
-
539
-		$result = $query->execute()->fetchAll();
540
-
541
-		if (!empty($result) && isset($result[0])) {
542
-			return $result[0];
543
-		}
544
-
545
-		return false;
546
-	}
547
-
548
-	/**
549
-	 * get file
550
-	 *
551
-	 * @param string $user
552
-	 * @param int $fileSource
553
-	 * @return array with internal path of the file and a absolute link to it
554
-	 */
555
-	private function getFile($user, $fileSource) {
556
-		\OC_Util::setupFS($user);
557
-
558
-		try {
559
-			$file = \OC\Files\Filesystem::getPath($fileSource);
560
-		} catch (NotFoundException $e) {
561
-			$file = null;
562
-		}
563
-		$args = \OC\Files\Filesystem::is_dir($file) ? array('dir' => $file) : array('dir' => dirname($file), 'scrollto' => $file);
564
-		$link = \OCP\Util::linkToAbsolute('files', 'index.php', $args);
565
-
566
-		return array($file, $link);
567
-
568
-	}
569
-
570
-	/**
571
-	 * check if server-to-server sharing is enabled
572
-	 *
573
-	 * @param bool $incoming
574
-	 * @return bool
575
-	 */
576
-	private function isS2SEnabled($incoming = false) {
577
-
578
-		$result = \OCP\App::isEnabled('files_sharing');
579
-
580
-		if ($incoming) {
581
-			$result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled();
582
-		} else {
583
-			$result = $result && $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
584
-		}
585
-
586
-		return $result;
587
-	}
588
-
589
-	/**
590
-	 * check if we got the right share
591
-	 *
592
-	 * @param Share\IShare $share
593
-	 * @param string $token
594
-	 * @return bool
595
-	 */
596
-	protected function verifyShare(Share\IShare $share, $token) {
597
-		if (
598
-			$share->getShareType() === FederatedShareProvider::SHARE_TYPE_REMOTE &&
599
-			$share->getToken() === $token
600
-		) {
601
-			return true;
602
-		}
603
-
604
-		return false;
605
-	}
606
-
607
-	/**
608
-	 * @NoCSRFRequired
609
-	 * @PublicPage
610
-	 *
611
-	 * update share information to keep federated re-shares in sync
612
-	 *
613
-	 * @param int $id
614
-	 * @return Http\DataResponse
615
-	 * @throws OCSBadRequestException
616
-	 */
617
-	public function updatePermissions($id) {
618
-		$token = $this->request->getParam('token', null);
619
-		$permissions = $this->request->getParam('permissions', null);
620
-
621
-		try {
622
-			$share = $this->federatedShareProvider->getShareById($id);
623
-		} catch (Share\Exceptions\ShareNotFound $e) {
624
-			throw new OCSBadRequestException();
625
-		}
626
-
627
-		$validPermission = ctype_digit($permissions);
628
-		$validToken = $this->verifyShare($share, $token);
629
-		if ($validPermission && $validToken) {
630
-			$this->updatePermissionsInDatabase($share, (int)$permissions);
631
-		} else {
632
-			throw new OCSBadRequestException();
633
-		}
634
-
635
-		return new Http\DataResponse();
636
-	}
637
-
638
-	/**
639
-	 * update permissions in database
640
-	 *
641
-	 * @param IShare $share
642
-	 * @param int $permissions
643
-	 */
644
-	protected function updatePermissionsInDatabase(IShare $share, $permissions) {
645
-		$query = $this->connection->getQueryBuilder();
646
-		$query->update('share')
647
-			->where($query->expr()->eq('id', $query->createNamedParameter($share->getId())))
648
-			->set('permissions', $query->createNamedParameter($permissions))
649
-			->execute();
650
-	}
651
-
652
-	/**
653
-	 * @NoCSRFRequired
654
-	 * @PublicPage
655
-	 *
656
-	 * change the owner of a server-to-server share
657
-	 *
658
-	 * @param int $id
659
-	 * @return Http\DataResponse
660
-	 * @throws \InvalidArgumentException
661
-	 * @throws OCSException
662
-	 */
663
-	public function move($id) {
664
-
665
-		if (!$this->isS2SEnabled()) {
666
-			throw new OCSException('Server does not support federated cloud sharing', 503);
667
-		}
668
-
669
-		$token = $this->request->getParam('token');
670
-		$remote = $this->request->getParam('remote');
671
-		$newRemoteId = $this->request->getParam('remote_id', $id);
672
-		$cloudId = $this->cloudIdManager->resolveCloudId($remote);
673
-
674
-		$qb = $this->connection->getQueryBuilder();
675
-		$query = $qb->update('share_external')
676
-			->set('remote', $qb->createNamedParameter($cloudId->getRemote()))
677
-			->set('owner', $qb->createNamedParameter($cloudId->getUser()))
678
-			->set('remote_id', $qb->createNamedParameter($newRemoteId))
679
-			->where($qb->expr()->eq('remote_id', $qb->createNamedParameter($id)))
680
-			->andWhere($qb->expr()->eq('share_token', $qb->createNamedParameter($token)));
681
-		$affected = $query->execute();
682
-
683
-		if ($affected > 0) {
684
-			return new Http\DataResponse(['remote' => $cloudId->getRemote(), 'owner' => $cloudId->getUser()]);
685
-		} else {
686
-			throw new OCSBadRequestException('Share not found or token invalid');
687
-		}
688
-	}
54
+    /** @var FederatedShareProvider */
55
+    private $federatedShareProvider;
56
+
57
+    /** @var IDBConnection */
58
+    private $connection;
59
+
60
+    /** @var Share\IManager */
61
+    private $shareManager;
62
+
63
+    /** @var Notifications */
64
+    private $notifications;
65
+
66
+    /** @var AddressHandler */
67
+    private $addressHandler;
68
+
69
+    /** @var  IUserManager */
70
+    private $userManager;
71
+
72
+    /** @var string */
73
+    private $shareTable = 'share';
74
+
75
+    /** @var ICloudIdManager */
76
+    private $cloudIdManager;
77
+
78
+    /** @var ILogger */
79
+    private $logger;
80
+
81
+    /**
82
+     * Server2Server constructor.
83
+     *
84
+     * @param string $appName
85
+     * @param IRequest $request
86
+     * @param FederatedShareProvider $federatedShareProvider
87
+     * @param IDBConnection $connection
88
+     * @param Share\IManager $shareManager
89
+     * @param Notifications $notifications
90
+     * @param AddressHandler $addressHandler
91
+     * @param IUserManager $userManager
92
+     * @param ICloudIdManager $cloudIdManager
93
+     */
94
+    public function __construct($appName,
95
+                                IRequest $request,
96
+                                FederatedShareProvider $federatedShareProvider,
97
+                                IDBConnection $connection,
98
+                                Share\IManager $shareManager,
99
+                                Notifications $notifications,
100
+                                AddressHandler $addressHandler,
101
+                                IUserManager $userManager,
102
+                                ICloudIdManager $cloudIdManager,
103
+                                ILogger $logger
104
+    ) {
105
+        parent::__construct($appName, $request);
106
+
107
+        $this->federatedShareProvider = $federatedShareProvider;
108
+        $this->connection = $connection;
109
+        $this->shareManager = $shareManager;
110
+        $this->notifications = $notifications;
111
+        $this->addressHandler = $addressHandler;
112
+        $this->userManager = $userManager;
113
+        $this->cloudIdManager = $cloudIdManager;
114
+        $this->logger = $logger;
115
+    }
116
+
117
+    /**
118
+     * @NoCSRFRequired
119
+     * @PublicPage
120
+     *
121
+     * create a new share
122
+     *
123
+     * @return Http\DataResponse
124
+     * @throws OCSException
125
+     */
126
+    public function createShare() {
127
+
128
+        if (!$this->isS2SEnabled(true)) {
129
+            throw new OCSException('Server does not support federated cloud sharing', 503);
130
+        }
131
+
132
+        $remote = isset($_POST['remote']) ? $_POST['remote'] : null;
133
+        $token = isset($_POST['token']) ? $_POST['token'] : null;
134
+        $name = isset($_POST['name']) ? $_POST['name'] : null;
135
+        $owner = isset($_POST['owner']) ? $_POST['owner'] : null;
136
+        $sharedBy = isset($_POST['sharedBy']) ? $_POST['sharedBy'] : null;
137
+        $shareWith = isset($_POST['shareWith']) ? $_POST['shareWith'] : null;
138
+        $remoteId = isset($_POST['remoteId']) ? (int)$_POST['remoteId'] : null;
139
+        $sharedByFederatedId = isset($_POST['sharedByFederatedId']) ? $_POST['sharedByFederatedId'] : null;
140
+        $ownerFederatedId = isset($_POST['ownerFederatedId']) ? $_POST['ownerFederatedId'] : null;
141
+
142
+        if ($remote && $token && $name && $owner && $remoteId && $shareWith) {
143
+
144
+            if (!\OCP\Util::isValidFileName($name)) {
145
+                throw new OCSException('The mountpoint name contains invalid characters.', 400);
146
+            }
147
+
148
+            // FIXME this should be a method in the user management instead
149
+            $this->logger->debug('shareWith before, ' . $shareWith, ['app' => 'files_sharing']);
150
+            \OCP\Util::emitHook(
151
+                '\OCA\Files_Sharing\API\Server2Server',
152
+                'preLoginNameUsedAsUserName',
153
+                array('uid' => &$shareWith)
154
+            );
155
+            $this->logger->debug('shareWith after, ' . $shareWith, ['app' => 'files_sharing']);
156
+
157
+            if (!\OC::$server->getUserManager()->userExists($shareWith)) {
158
+                throw new OCSException('User does not exists', 400);
159
+            }
160
+
161
+            \OC_Util::setupFS($shareWith);
162
+
163
+            $externalManager = new \OCA\Files_Sharing\External\Manager(
164
+                    \OC::$server->getDatabaseConnection(),
165
+                    \OC\Files\Filesystem::getMountManager(),
166
+                    \OC\Files\Filesystem::getLoader(),
167
+                    \OC::$server->getHTTPClientService(),
168
+                    \OC::$server->getNotificationManager(),
169
+                    \OC::$server->query(\OCP\OCS\IDiscoveryService::class),
170
+                    $shareWith
171
+                );
172
+
173
+            try {
174
+                $externalManager->addShare($remote, $token, '', $name, $owner, false, $shareWith, $remoteId);
175
+                $shareId = \OC::$server->getDatabaseConnection()->lastInsertId('*PREFIX*share_external');
176
+
177
+                if ($ownerFederatedId === null) {
178
+                    $ownerFederatedId = $this->cloudIdManager->getCloudId($owner, $this->cleanupRemote($remote))->getId();
179
+                }
180
+                // if the owner of the share and the initiator are the same user
181
+                // we also complete the federated share ID for the initiator
182
+                if ($sharedByFederatedId === null && $owner === $sharedBy) {
183
+                    $sharedByFederatedId = $ownerFederatedId;
184
+                }
185
+
186
+                $event = \OC::$server->getActivityManager()->generateEvent();
187
+                $event->setApp('files_sharing')
188
+                    ->setType('remote_share')
189
+                    ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_RECEIVED, [$ownerFederatedId, trim($name, '/')])
190
+                    ->setAffectedUser($shareWith)
191
+                    ->setObject('remote_share', (int)$shareId, $name);
192
+                \OC::$server->getActivityManager()->publish($event);
193
+
194
+                $urlGenerator = \OC::$server->getURLGenerator();
195
+
196
+                $notificationManager = \OC::$server->getNotificationManager();
197
+                $notification = $notificationManager->createNotification();
198
+                $notification->setApp('files_sharing')
199
+                    ->setUser($shareWith)
200
+                    ->setDateTime(new \DateTime())
201
+                    ->setObject('remote_share', $shareId)
202
+                    ->setSubject('remote_share', [$ownerFederatedId, $sharedByFederatedId, trim($name, '/')]);
203
+
204
+                $declineAction = $notification->createAction();
205
+                $declineAction->setLabel('decline')
206
+                    ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'DELETE');
207
+                $notification->addAction($declineAction);
208
+
209
+                $acceptAction = $notification->createAction();
210
+                $acceptAction->setLabel('accept')
211
+                    ->setLink($urlGenerator->getAbsoluteURL($urlGenerator->linkTo('', 'ocs/v2.php/apps/files_sharing/api/v1/remote_shares/pending/' . $shareId)), 'POST');
212
+                $notification->addAction($acceptAction);
213
+
214
+                $notificationManager->notify($notification);
215
+
216
+                return new Http\DataResponse();
217
+            } catch (\Exception $e) {
218
+                $this->logger->logException($e, [
219
+                    'message' => 'Server can not add remote share.',
220
+                    'level' => ILogger::ERROR,
221
+                    'app' => 'files_sharing'
222
+                ]);
223
+                throw new OCSException('internal server error, was not able to add share from ' . $remote, 500);
224
+            }
225
+        }
226
+
227
+        throw new OCSException('server can not add remote share, missing parameter', 400);
228
+    }
229
+
230
+    /**
231
+     * @NoCSRFRequired
232
+     * @PublicPage
233
+     *
234
+     * create re-share on behalf of another user
235
+     *
236
+     * @param int $id
237
+     * @return Http\DataResponse
238
+     * @throws OCSBadRequestException
239
+     * @throws OCSForbiddenException
240
+     * @throws OCSNotFoundException
241
+     */
242
+    public function reShare($id) {
243
+
244
+        $token = $this->request->getParam('token', null);
245
+        $shareWith = $this->request->getParam('shareWith', null);
246
+        $permission = (int)$this->request->getParam('permission', null);
247
+        $remoteId = (int)$this->request->getParam('remoteId', null);
248
+
249
+        if ($id === null ||
250
+            $token === null ||
251
+            $shareWith === null ||
252
+            $permission === null ||
253
+            $remoteId === null
254
+        ) {
255
+            throw new OCSBadRequestException();
256
+        }
257
+
258
+        try {
259
+            $share = $this->federatedShareProvider->getShareById($id);
260
+        } catch (Share\Exceptions\ShareNotFound $e) {
261
+            throw new OCSNotFoundException();
262
+        }
263
+
264
+        // don't allow to share a file back to the owner
265
+        list($user, $remote) = $this->addressHandler->splitUserRemote($shareWith);
266
+        $owner = $share->getShareOwner();
267
+        $currentServer = $this->addressHandler->generateRemoteURL();
268
+        if ($this->addressHandler->compareAddresses($user, $remote, $owner, $currentServer)) {
269
+            throw new OCSForbiddenException();
270
+        }
271
+
272
+        if ($this->verifyShare($share, $token)) {
273
+
274
+            // check if re-sharing is allowed
275
+            if ($share->getPermissions() | ~Constants::PERMISSION_SHARE) {
276
+                $share->setPermissions($share->getPermissions() & $permission);
277
+                // the recipient of the initial share is now the initiator for the re-share
278
+                $share->setSharedBy($share->getSharedWith());
279
+                $share->setSharedWith($shareWith);
280
+                try {
281
+                    $result = $this->federatedShareProvider->create($share);
282
+                    $this->federatedShareProvider->storeRemoteId((int)$result->getId(), $remoteId);
283
+                    return new Http\DataResponse([
284
+                        'token' => $result->getToken(),
285
+                        'remoteId' => $result->getId()
286
+                    ]);
287
+                } catch (\Exception $e) {
288
+                    throw new OCSBadRequestException();
289
+                }
290
+            } else {
291
+                throw new OCSForbiddenException();
292
+            }
293
+        }
294
+        throw new OCSBadRequestException();
295
+    }
296
+
297
+    /**
298
+     * @NoCSRFRequired
299
+     * @PublicPage
300
+     *
301
+     * accept server-to-server share
302
+     *
303
+     * @param int $id
304
+     * @return Http\DataResponse
305
+     * @throws OCSException
306
+     */
307
+    public function acceptShare($id) {
308
+
309
+        if (!$this->isS2SEnabled()) {
310
+            throw new OCSException('Server does not support federated cloud sharing', 503);
311
+        }
312
+
313
+        $token = isset($_POST['token']) ? $_POST['token'] : null;
314
+
315
+        try {
316
+            $share = $this->federatedShareProvider->getShareById($id);
317
+        } catch (Share\Exceptions\ShareNotFound $e) {
318
+            return new Http\DataResponse();
319
+        }
320
+
321
+        if ($this->verifyShare($share, $token)) {
322
+            $this->executeAcceptShare($share);
323
+            if ($share->getShareOwner() !== $share->getSharedBy()) {
324
+                list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
325
+                $remoteId = $this->federatedShareProvider->getRemoteId($share);
326
+                $this->notifications->sendAcceptShare($remote, $remoteId, $share->getToken());
327
+            }
328
+        }
329
+
330
+        return new Http\DataResponse();
331
+    }
332
+
333
+    protected function executeAcceptShare(Share\IShare $share) {
334
+        $fileId = (int) $share->getNode()->getId();
335
+        list($file, $link) = $this->getFile($this->getCorrectUid($share), $fileId);
336
+
337
+        $event = \OC::$server->getActivityManager()->generateEvent();
338
+        $event->setApp('files_sharing')
339
+            ->setType('remote_share')
340
+            ->setAffectedUser($this->getCorrectUid($share))
341
+            ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_ACCEPTED, [$share->getSharedWith(), [$fileId => $file]])
342
+            ->setObject('files', $fileId, $file)
343
+            ->setLink($link);
344
+        \OC::$server->getActivityManager()->publish($event);
345
+    }
346
+
347
+    /**
348
+     * @NoCSRFRequired
349
+     * @PublicPage
350
+     *
351
+     * decline server-to-server share
352
+     *
353
+     * @param int $id
354
+     * @return Http\DataResponse
355
+     * @throws OCSException
356
+     */
357
+    public function declineShare($id) {
358
+
359
+        if (!$this->isS2SEnabled()) {
360
+            throw new OCSException('Server does not support federated cloud sharing', 503);
361
+        }
362
+
363
+        $token = isset($_POST['token']) ? $_POST['token'] : null;
364
+
365
+        try {
366
+            $share = $this->federatedShareProvider->getShareById($id);
367
+        } catch (Share\Exceptions\ShareNotFound $e) {
368
+            return new Http\DataResponse();
369
+        }
370
+
371
+        if ($this->verifyShare($share, $token)) {
372
+            if ($share->getShareOwner() !== $share->getSharedBy()) {
373
+                list(, $remote) = $this->addressHandler->splitUserRemote($share->getSharedBy());
374
+                $remoteId = $this->federatedShareProvider->getRemoteId($share);
375
+                $this->notifications->sendDeclineShare($remote, $remoteId, $share->getToken());
376
+            }
377
+            $this->executeDeclineShare($share);
378
+        }
379
+
380
+        return new Http\DataResponse();
381
+    }
382
+
383
+    /**
384
+     * delete declined share and create a activity
385
+     *
386
+     * @param Share\IShare $share
387
+     */
388
+    protected function executeDeclineShare(Share\IShare $share) {
389
+        $this->federatedShareProvider->removeShareFromTable($share);
390
+        $fileId = (int) $share->getNode()->getId();
391
+        list($file, $link) = $this->getFile($this->getCorrectUid($share), $fileId);
392
+
393
+        $event = \OC::$server->getActivityManager()->generateEvent();
394
+        $event->setApp('files_sharing')
395
+            ->setType('remote_share')
396
+            ->setAffectedUser($this->getCorrectUid($share))
397
+            ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_DECLINED, [$share->getSharedWith(), [$fileId => $file]])
398
+            ->setObject('files', $fileId, $file)
399
+            ->setLink($link);
400
+        \OC::$server->getActivityManager()->publish($event);
401
+
402
+    }
403
+
404
+    /**
405
+     * check if we are the initiator or the owner of a re-share and return the correct UID
406
+     *
407
+     * @param Share\IShare $share
408
+     * @return string
409
+     */
410
+    protected function getCorrectUid(Share\IShare $share) {
411
+        if ($this->userManager->userExists($share->getShareOwner())) {
412
+            return $share->getShareOwner();
413
+        }
414
+
415
+        return $share->getSharedBy();
416
+    }
417
+
418
+    /**
419
+     * @NoCSRFRequired
420
+     * @PublicPage
421
+     *
422
+     * remove server-to-server share if it was unshared by the owner
423
+     *
424
+     * @param int $id
425
+     * @return Http\DataResponse
426
+     * @throws OCSException
427
+     */
428
+    public function unshare($id) {
429
+
430
+        if (!$this->isS2SEnabled()) {
431
+            throw new OCSException('Server does not support federated cloud sharing', 503);
432
+        }
433
+
434
+        $token = isset($_POST['token']) ? $_POST['token'] : null;
435
+
436
+        $qb = $this->connection->getQueryBuilder();
437
+        $qb->select('*')
438
+            ->from('share_external')
439
+            ->where(
440
+                $qb->expr()->andX(
441
+                    $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)),
442
+                    $qb->expr()->eq('share_token', $qb->createNamedParameter($token))
443
+                )
444
+            );
445
+
446
+        $result = $qb->execute();
447
+        $share = $result->fetch();
448
+        $result->closeCursor();
449
+
450
+        if ($token && $id && !empty($share)) {
451
+
452
+            $remote = $this->cleanupRemote($share['remote']);
453
+
454
+            $owner = $this->cloudIdManager->getCloudId($share['owner'], $remote);
455
+            $mountpoint = $share['mountpoint'];
456
+            $user = $share['user'];
457
+
458
+            $qb = $this->connection->getQueryBuilder();
459
+            $qb->delete('share_external')
460
+                ->where(
461
+                    $qb->expr()->andX(
462
+                        $qb->expr()->eq('remote_id', $qb->createNamedParameter($id)),
463
+                        $qb->expr()->eq('share_token', $qb->createNamedParameter($token))
464
+                    )
465
+                );
466
+
467
+            $result = $qb->execute();
468
+            $result->closeCursor();
469
+
470
+            if ($share['accepted']) {
471
+                $path = trim($mountpoint, '/');
472
+            } else {
473
+                $path = trim($share['name'], '/');
474
+            }
475
+
476
+            $notificationManager = \OC::$server->getNotificationManager();
477
+            $notification = $notificationManager->createNotification();
478
+            $notification->setApp('files_sharing')
479
+                ->setUser($share['user'])
480
+                ->setObject('remote_share', (int)$share['id']);
481
+            $notificationManager->markProcessed($notification);
482
+
483
+            $event = \OC::$server->getActivityManager()->generateEvent();
484
+            $event->setApp('files_sharing')
485
+                ->setType('remote_share')
486
+                ->setSubject(RemoteShares::SUBJECT_REMOTE_SHARE_UNSHARED, [$owner->getId(), $path])
487
+                ->setAffectedUser($user)
488
+                ->setObject('remote_share', (int)$share['id'], $path);
489
+            \OC::$server->getActivityManager()->publish($event);
490
+        }
491
+
492
+        return new Http\DataResponse();
493
+    }
494
+
495
+    private function cleanupRemote($remote) {
496
+        $remote = substr($remote, strpos($remote, '://') + 3);
497
+
498
+        return rtrim($remote, '/');
499
+    }
500
+
501
+
502
+    /**
503
+     * @NoCSRFRequired
504
+     * @PublicPage
505
+     *
506
+     * federated share was revoked, either by the owner or the re-sharer
507
+     *
508
+     * @param int $id
509
+     * @return Http\DataResponse
510
+     * @throws OCSBadRequestException
511
+     */
512
+    public function revoke($id) {
513
+        $token = $this->request->getParam('token');
514
+
515
+        $share = $this->federatedShareProvider->getShareById($id);
516
+
517
+        if ($this->verifyShare($share, $token)) {
518
+            $this->federatedShareProvider->removeShareFromTable($share);
519
+            return new Http\DataResponse();
520
+        }
521
+
522
+        throw new OCSBadRequestException();
523
+    }
524
+
525
+    /**
526
+     * get share
527
+     *
528
+     * @param int $id
529
+     * @param string $token
530
+     * @return array|bool
531
+     */
532
+    protected function getShare($id, $token) {
533
+        $query = $this->connection->getQueryBuilder();
534
+        $query->select('*')->from($this->shareTable)
535
+            ->where($query->expr()->eq('token', $query->createNamedParameter($token)))
536
+            ->andWhere($query->expr()->eq('share_type', $query->createNamedParameter(FederatedShareProvider::SHARE_TYPE_REMOTE)))
537
+            ->andWhere($query->expr()->eq('id', $query->createNamedParameter($id)));
538
+
539
+        $result = $query->execute()->fetchAll();
540
+
541
+        if (!empty($result) && isset($result[0])) {
542
+            return $result[0];
543
+        }
544
+
545
+        return false;
546
+    }
547
+
548
+    /**
549
+     * get file
550
+     *
551
+     * @param string $user
552
+     * @param int $fileSource
553
+     * @return array with internal path of the file and a absolute link to it
554
+     */
555
+    private function getFile($user, $fileSource) {
556
+        \OC_Util::setupFS($user);
557
+
558
+        try {
559
+            $file = \OC\Files\Filesystem::getPath($fileSource);
560
+        } catch (NotFoundException $e) {
561
+            $file = null;
562
+        }
563
+        $args = \OC\Files\Filesystem::is_dir($file) ? array('dir' => $file) : array('dir' => dirname($file), 'scrollto' => $file);
564
+        $link = \OCP\Util::linkToAbsolute('files', 'index.php', $args);
565
+
566
+        return array($file, $link);
567
+
568
+    }
569
+
570
+    /**
571
+     * check if server-to-server sharing is enabled
572
+     *
573
+     * @param bool $incoming
574
+     * @return bool
575
+     */
576
+    private function isS2SEnabled($incoming = false) {
577
+
578
+        $result = \OCP\App::isEnabled('files_sharing');
579
+
580
+        if ($incoming) {
581
+            $result = $result && $this->federatedShareProvider->isIncomingServer2serverShareEnabled();
582
+        } else {
583
+            $result = $result && $this->federatedShareProvider->isOutgoingServer2serverShareEnabled();
584
+        }
585
+
586
+        return $result;
587
+    }
588
+
589
+    /**
590
+     * check if we got the right share
591
+     *
592
+     * @param Share\IShare $share
593
+     * @param string $token
594
+     * @return bool
595
+     */
596
+    protected function verifyShare(Share\IShare $share, $token) {
597
+        if (
598
+            $share->getShareType() === FederatedShareProvider::SHARE_TYPE_REMOTE &&
599
+            $share->getToken() === $token
600
+        ) {
601
+            return true;
602
+        }
603
+
604
+        return false;
605
+    }
606
+
607
+    /**
608
+     * @NoCSRFRequired
609
+     * @PublicPage
610
+     *
611
+     * update share information to keep federated re-shares in sync
612
+     *
613
+     * @param int $id
614
+     * @return Http\DataResponse
615
+     * @throws OCSBadRequestException
616
+     */
617
+    public function updatePermissions($id) {
618
+        $token = $this->request->getParam('token', null);
619
+        $permissions = $this->request->getParam('permissions', null);
620
+
621
+        try {
622
+            $share = $this->federatedShareProvider->getShareById($id);
623
+        } catch (Share\Exceptions\ShareNotFound $e) {
624
+            throw new OCSBadRequestException();
625
+        }
626
+
627
+        $validPermission = ctype_digit($permissions);
628
+        $validToken = $this->verifyShare($share, $token);
629
+        if ($validPermission && $validToken) {
630
+            $this->updatePermissionsInDatabase($share, (int)$permissions);
631
+        } else {
632
+            throw new OCSBadRequestException();
633
+        }
634
+
635
+        return new Http\DataResponse();
636
+    }
637
+
638
+    /**
639
+     * update permissions in database
640
+     *
641
+     * @param IShare $share
642
+     * @param int $permissions
643
+     */
644
+    protected function updatePermissionsInDatabase(IShare $share, $permissions) {
645
+        $query = $this->connection->getQueryBuilder();
646
+        $query->update('share')
647
+            ->where($query->expr()->eq('id', $query->createNamedParameter($share->getId())))
648
+            ->set('permissions', $query->createNamedParameter($permissions))
649
+            ->execute();
650
+    }
651
+
652
+    /**
653
+     * @NoCSRFRequired
654
+     * @PublicPage
655
+     *
656
+     * change the owner of a server-to-server share
657
+     *
658
+     * @param int $id
659
+     * @return Http\DataResponse
660
+     * @throws \InvalidArgumentException
661
+     * @throws OCSException
662
+     */
663
+    public function move($id) {
664
+
665
+        if (!$this->isS2SEnabled()) {
666
+            throw new OCSException('Server does not support federated cloud sharing', 503);
667
+        }
668
+
669
+        $token = $this->request->getParam('token');
670
+        $remote = $this->request->getParam('remote');
671
+        $newRemoteId = $this->request->getParam('remote_id', $id);
672
+        $cloudId = $this->cloudIdManager->resolveCloudId($remote);
673
+
674
+        $qb = $this->connection->getQueryBuilder();
675
+        $query = $qb->update('share_external')
676
+            ->set('remote', $qb->createNamedParameter($cloudId->getRemote()))
677
+            ->set('owner', $qb->createNamedParameter($cloudId->getUser()))
678
+            ->set('remote_id', $qb->createNamedParameter($newRemoteId))
679
+            ->where($qb->expr()->eq('remote_id', $qb->createNamedParameter($id)))
680
+            ->andWhere($qb->expr()->eq('share_token', $qb->createNamedParameter($token)));
681
+        $affected = $query->execute();
682
+
683
+        if ($affected > 0) {
684
+            return new Http\DataResponse(['remote' => $cloudId->getRemote(), 'owner' => $cloudId->getUser()]);
685
+        } else {
686
+            throw new OCSBadRequestException('Share not found or token invalid');
687
+        }
688
+    }
689 689
 }
Please login to merge, or discard this patch.
apps/federatedfilesharing/lib/Controller/MountPublicLinkController.php 1 patch
Indentation   +173 added lines, -173 removed lines patch added patch discarded remove patch
@@ -58,177 +58,177 @@
 block discarded – undo
58 58
  */
59 59
 class MountPublicLinkController extends Controller {
60 60
 
61
-	/** @var FederatedShareProvider */
62
-	private $federatedShareProvider;
63
-
64
-	/** @var AddressHandler */
65
-	private $addressHandler;
66
-
67
-	/** @var IManager  */
68
-	private $shareManager;
69
-
70
-	/** @var  ISession */
71
-	private $session;
72
-
73
-	/** @var IL10N */
74
-	private $l;
75
-
76
-	/** @var IUserSession */
77
-	private $userSession;
78
-
79
-	/** @var IClientService */
80
-	private $clientService;
81
-
82
-	/** @var ICloudIdManager  */
83
-	private $cloudIdManager;
84
-
85
-	/**
86
-	 * MountPublicLinkController constructor.
87
-	 *
88
-	 * @param string $appName
89
-	 * @param IRequest $request
90
-	 * @param FederatedShareProvider $federatedShareProvider
91
-	 * @param IManager $shareManager
92
-	 * @param AddressHandler $addressHandler
93
-	 * @param ISession $session
94
-	 * @param IL10N $l
95
-	 * @param IUserSession $userSession
96
-	 * @param IClientService $clientService
97
-	 * @param ICloudIdManager $cloudIdManager
98
-	 */
99
-	public function __construct($appName,
100
-								IRequest $request,
101
-								FederatedShareProvider $federatedShareProvider,
102
-								IManager $shareManager,
103
-								AddressHandler $addressHandler,
104
-								ISession $session,
105
-								IL10N $l,
106
-								IUserSession $userSession,
107
-								IClientService $clientService,
108
-								ICloudIdManager $cloudIdManager
109
-	) {
110
-		parent::__construct($appName, $request);
111
-
112
-		$this->federatedShareProvider = $federatedShareProvider;
113
-		$this->shareManager = $shareManager;
114
-		$this->addressHandler = $addressHandler;
115
-		$this->session = $session;
116
-		$this->l = $l;
117
-		$this->userSession = $userSession;
118
-		$this->clientService = $clientService;
119
-		$this->cloudIdManager = $cloudIdManager;
120
-	}
121
-
122
-	/**
123
-	 * send federated share to a user of a public link
124
-	 *
125
-	 * @NoCSRFRequired
126
-	 * @PublicPage
127
-	 * @BruteForceProtection(action=publicLink2FederatedShare)
128
-	 *
129
-	 * @param string $shareWith
130
-	 * @param string $token
131
-	 * @param string $password
132
-	 * @return JSONResponse
133
-	 */
134
-	public function createFederatedShare($shareWith, $token, $password = '') {
135
-
136
-		if (!$this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) {
137
-			return new JSONResponse(
138
-				['message' => 'This server doesn\'t support outgoing federated shares'],
139
-				Http::STATUS_BAD_REQUEST
140
-			);
141
-		}
142
-
143
-		try {
144
-			list(, $server) = $this->addressHandler->splitUserRemote($shareWith);
145
-			$share = $this->shareManager->getShareByToken($token);
146
-		} catch (HintException $e) {
147
-			return new JSONResponse(['message' => $e->getHint()], Http::STATUS_BAD_REQUEST);
148
-		}
149
-
150
-		// make sure that user is authenticated in case of a password protected link
151
-		$storedPassword = $share->getPassword();
152
-		$authenticated = $this->session->get('public_link_authenticated') === $share->getId() ||
153
-			$this->shareManager->checkPassword($share, $password);
154
-		if (!empty($storedPassword) && !$authenticated ) {
155
-			$response = new JSONResponse(
156
-				['message' => 'No permission to access the share'],
157
-				Http::STATUS_BAD_REQUEST
158
-			);
159
-			$response->throttle();
160
-			return $response;
161
-		}
162
-
163
-		$share->setSharedWith($shareWith);
164
-
165
-		try {
166
-			$this->federatedShareProvider->create($share);
167
-		} catch (\Exception $e) {
168
-			\OC::$server->getLogger()->logException($e, [
169
-				'level' => ILogger::WARN,
170
-				'app' => 'federatedfilesharing',
171
-			]);
172
-			return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
173
-		}
174
-
175
-		return new JSONResponse(['remoteUrl' => $server]);
176
-	}
177
-
178
-	/**
179
-	 * ask other server to get a federated share
180
-	 *
181
-	 * @NoAdminRequired
182
-	 *
183
-	 * @param string $token
184
-	 * @param string $remote
185
-	 * @param string $password
186
-	 * @param string $owner (only for legacy reasons, can be removed with legacyMountPublicLink())
187
-	 * @param string $ownerDisplayName (only for legacy reasons, can be removed with legacyMountPublicLink())
188
-	 * @param string $name (only for legacy reasons, can be removed with legacyMountPublicLink())
189
-	 * @return JSONResponse
190
-	 */
191
-	public function askForFederatedShare($token, $remote, $password = '', $owner = '', $ownerDisplayName = '', $name = '') {
192
-		// check if server admin allows to mount public links from other servers
193
-		if ($this->federatedShareProvider->isIncomingServer2serverShareEnabled() === false) {
194
-			return new JSONResponse(['message' => $this->l->t('Server to server sharing is not enabled on this server')], Http::STATUS_BAD_REQUEST);
195
-		}
196
-
197
-		$cloudId = $this->cloudIdManager->getCloudId($this->userSession->getUser()->getUID(), $this->addressHandler->generateRemoteURL());
198
-
199
-		$httpClient = $this->clientService->newClient();
200
-
201
-		try {
202
-			$response = $httpClient->post($remote . '/index.php/apps/federatedfilesharing/createFederatedShare',
203
-				[
204
-					'body' =>
205
-						[
206
-							'token' => $token,
207
-							'shareWith' => rtrim($cloudId->getId(), '/'),
208
-							'password' => $password
209
-						],
210
-					'connect_timeout' => 10,
211
-				]
212
-			);
213
-		} catch (\Exception $e) {
214
-			if (empty($password)) {
215
-				$message = $this->l->t("Couldn't establish a federated share.");
216
-			} else {
217
-				$message = $this->l->t("Couldn't establish a federated share, maybe the password was wrong.");
218
-			}
219
-			return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
220
-		}
221
-
222
-		$body = $response->getBody();
223
-		$result = json_decode($body, true);
224
-
225
-		if (is_array($result) && isset($result['remoteUrl'])) {
226
-			return new JSONResponse(['message' => $this->l->t('Federated Share request sent, you will receive an invitation. Check your notifications.')]);
227
-		}
228
-
229
-		// if we doesn't get the expected response we assume that we try to add
230
-		// a federated share from a Nextcloud <= 9 server
231
-		$message = $this->l->t("Couldn't establish a federated share, it looks like the server to federate with is too old (Nextcloud <= 9).");
232
-		return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
233
-	}
61
+    /** @var FederatedShareProvider */
62
+    private $federatedShareProvider;
63
+
64
+    /** @var AddressHandler */
65
+    private $addressHandler;
66
+
67
+    /** @var IManager  */
68
+    private $shareManager;
69
+
70
+    /** @var  ISession */
71
+    private $session;
72
+
73
+    /** @var IL10N */
74
+    private $l;
75
+
76
+    /** @var IUserSession */
77
+    private $userSession;
78
+
79
+    /** @var IClientService */
80
+    private $clientService;
81
+
82
+    /** @var ICloudIdManager  */
83
+    private $cloudIdManager;
84
+
85
+    /**
86
+     * MountPublicLinkController constructor.
87
+     *
88
+     * @param string $appName
89
+     * @param IRequest $request
90
+     * @param FederatedShareProvider $federatedShareProvider
91
+     * @param IManager $shareManager
92
+     * @param AddressHandler $addressHandler
93
+     * @param ISession $session
94
+     * @param IL10N $l
95
+     * @param IUserSession $userSession
96
+     * @param IClientService $clientService
97
+     * @param ICloudIdManager $cloudIdManager
98
+     */
99
+    public function __construct($appName,
100
+                                IRequest $request,
101
+                                FederatedShareProvider $federatedShareProvider,
102
+                                IManager $shareManager,
103
+                                AddressHandler $addressHandler,
104
+                                ISession $session,
105
+                                IL10N $l,
106
+                                IUserSession $userSession,
107
+                                IClientService $clientService,
108
+                                ICloudIdManager $cloudIdManager
109
+    ) {
110
+        parent::__construct($appName, $request);
111
+
112
+        $this->federatedShareProvider = $federatedShareProvider;
113
+        $this->shareManager = $shareManager;
114
+        $this->addressHandler = $addressHandler;
115
+        $this->session = $session;
116
+        $this->l = $l;
117
+        $this->userSession = $userSession;
118
+        $this->clientService = $clientService;
119
+        $this->cloudIdManager = $cloudIdManager;
120
+    }
121
+
122
+    /**
123
+     * send federated share to a user of a public link
124
+     *
125
+     * @NoCSRFRequired
126
+     * @PublicPage
127
+     * @BruteForceProtection(action=publicLink2FederatedShare)
128
+     *
129
+     * @param string $shareWith
130
+     * @param string $token
131
+     * @param string $password
132
+     * @return JSONResponse
133
+     */
134
+    public function createFederatedShare($shareWith, $token, $password = '') {
135
+
136
+        if (!$this->federatedShareProvider->isOutgoingServer2serverShareEnabled()) {
137
+            return new JSONResponse(
138
+                ['message' => 'This server doesn\'t support outgoing federated shares'],
139
+                Http::STATUS_BAD_REQUEST
140
+            );
141
+        }
142
+
143
+        try {
144
+            list(, $server) = $this->addressHandler->splitUserRemote($shareWith);
145
+            $share = $this->shareManager->getShareByToken($token);
146
+        } catch (HintException $e) {
147
+            return new JSONResponse(['message' => $e->getHint()], Http::STATUS_BAD_REQUEST);
148
+        }
149
+
150
+        // make sure that user is authenticated in case of a password protected link
151
+        $storedPassword = $share->getPassword();
152
+        $authenticated = $this->session->get('public_link_authenticated') === $share->getId() ||
153
+            $this->shareManager->checkPassword($share, $password);
154
+        if (!empty($storedPassword) && !$authenticated ) {
155
+            $response = new JSONResponse(
156
+                ['message' => 'No permission to access the share'],
157
+                Http::STATUS_BAD_REQUEST
158
+            );
159
+            $response->throttle();
160
+            return $response;
161
+        }
162
+
163
+        $share->setSharedWith($shareWith);
164
+
165
+        try {
166
+            $this->federatedShareProvider->create($share);
167
+        } catch (\Exception $e) {
168
+            \OC::$server->getLogger()->logException($e, [
169
+                'level' => ILogger::WARN,
170
+                'app' => 'federatedfilesharing',
171
+            ]);
172
+            return new JSONResponse(['message' => $e->getMessage()], Http::STATUS_BAD_REQUEST);
173
+        }
174
+
175
+        return new JSONResponse(['remoteUrl' => $server]);
176
+    }
177
+
178
+    /**
179
+     * ask other server to get a federated share
180
+     *
181
+     * @NoAdminRequired
182
+     *
183
+     * @param string $token
184
+     * @param string $remote
185
+     * @param string $password
186
+     * @param string $owner (only for legacy reasons, can be removed with legacyMountPublicLink())
187
+     * @param string $ownerDisplayName (only for legacy reasons, can be removed with legacyMountPublicLink())
188
+     * @param string $name (only for legacy reasons, can be removed with legacyMountPublicLink())
189
+     * @return JSONResponse
190
+     */
191
+    public function askForFederatedShare($token, $remote, $password = '', $owner = '', $ownerDisplayName = '', $name = '') {
192
+        // check if server admin allows to mount public links from other servers
193
+        if ($this->federatedShareProvider->isIncomingServer2serverShareEnabled() === false) {
194
+            return new JSONResponse(['message' => $this->l->t('Server to server sharing is not enabled on this server')], Http::STATUS_BAD_REQUEST);
195
+        }
196
+
197
+        $cloudId = $this->cloudIdManager->getCloudId($this->userSession->getUser()->getUID(), $this->addressHandler->generateRemoteURL());
198
+
199
+        $httpClient = $this->clientService->newClient();
200
+
201
+        try {
202
+            $response = $httpClient->post($remote . '/index.php/apps/federatedfilesharing/createFederatedShare',
203
+                [
204
+                    'body' =>
205
+                        [
206
+                            'token' => $token,
207
+                            'shareWith' => rtrim($cloudId->getId(), '/'),
208
+                            'password' => $password
209
+                        ],
210
+                    'connect_timeout' => 10,
211
+                ]
212
+            );
213
+        } catch (\Exception $e) {
214
+            if (empty($password)) {
215
+                $message = $this->l->t("Couldn't establish a federated share.");
216
+            } else {
217
+                $message = $this->l->t("Couldn't establish a federated share, maybe the password was wrong.");
218
+            }
219
+            return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
220
+        }
221
+
222
+        $body = $response->getBody();
223
+        $result = json_decode($body, true);
224
+
225
+        if (is_array($result) && isset($result['remoteUrl'])) {
226
+            return new JSONResponse(['message' => $this->l->t('Federated Share request sent, you will receive an invitation. Check your notifications.')]);
227
+        }
228
+
229
+        // if we doesn't get the expected response we assume that we try to add
230
+        // a federated share from a Nextcloud <= 9 server
231
+        $message = $this->l->t("Couldn't establish a federated share, it looks like the server to federate with is too old (Nextcloud <= 9).");
232
+        return new JSONResponse(['message' => $message], Http::STATUS_BAD_REQUEST);
233
+    }
234 234
 }
Please login to merge, or discard this patch.
apps/sharebymail/lib/ShareByMailProvider.php 1 patch
Indentation   +1018 added lines, -1018 removed lines patch added patch discarded remove patch
@@ -53,1036 +53,1036 @@
 block discarded – undo
53 53
  */
54 54
 class ShareByMailProvider implements IShareProvider {
55 55
 
56
-	/** @var  IDBConnection */
57
-	private $dbConnection;
58
-
59
-	/** @var ILogger */
60
-	private $logger;
61
-
62
-	/** @var ISecureRandom */
63
-	private $secureRandom;
64
-
65
-	/** @var IUserManager */
66
-	private $userManager;
67
-
68
-	/** @var IRootFolder */
69
-	private $rootFolder;
70
-
71
-	/** @var IL10N */
72
-	private $l;
73
-
74
-	/** @var IMailer */
75
-	private $mailer;
76
-
77
-	/** @var IURLGenerator */
78
-	private $urlGenerator;
79
-
80
-	/** @var IManager  */
81
-	private $activityManager;
82
-
83
-	/** @var SettingsManager */
84
-	private $settingsManager;
85
-
86
-	/** @var Defaults */
87
-	private $defaults;
88
-
89
-	/** @var IHasher */
90
-	private $hasher;
91
-
92
-	/** @var  CapabilitiesManager */
93
-	private $capabilitiesManager;
94
-
95
-	/**
96
-	 * Return the identifier of this provider.
97
-	 *
98
-	 * @return string Containing only [a-zA-Z0-9]
99
-	 */
100
-	public function identifier() {
101
-		return 'ocMailShare';
102
-	}
103
-
104
-	/**
105
-	 * DefaultShareProvider constructor.
106
-	 *
107
-	 * @param IDBConnection $connection
108
-	 * @param ISecureRandom $secureRandom
109
-	 * @param IUserManager $userManager
110
-	 * @param IRootFolder $rootFolder
111
-	 * @param IL10N $l
112
-	 * @param ILogger $logger
113
-	 * @param IMailer $mailer
114
-	 * @param IURLGenerator $urlGenerator
115
-	 * @param IManager $activityManager
116
-	 * @param SettingsManager $settingsManager
117
-	 * @param Defaults $defaults
118
-	 * @param IHasher $hasher
119
-	 * @param CapabilitiesManager $capabilitiesManager
120
-	 */
121
-	public function __construct(
122
-		IDBConnection $connection,
123
-		ISecureRandom $secureRandom,
124
-		IUserManager $userManager,
125
-		IRootFolder $rootFolder,
126
-		IL10N $l,
127
-		ILogger $logger,
128
-		IMailer $mailer,
129
-		IURLGenerator $urlGenerator,
130
-		IManager $activityManager,
131
-		SettingsManager $settingsManager,
132
-		Defaults $defaults,
133
-		IHasher $hasher,
134
-		CapabilitiesManager $capabilitiesManager
135
-	) {
136
-		$this->dbConnection = $connection;
137
-		$this->secureRandom = $secureRandom;
138
-		$this->userManager = $userManager;
139
-		$this->rootFolder = $rootFolder;
140
-		$this->l = $l;
141
-		$this->logger = $logger;
142
-		$this->mailer = $mailer;
143
-		$this->urlGenerator = $urlGenerator;
144
-		$this->activityManager = $activityManager;
145
-		$this->settingsManager = $settingsManager;
146
-		$this->defaults = $defaults;
147
-		$this->hasher = $hasher;
148
-		$this->capabilitiesManager = $capabilitiesManager;
149
-	}
150
-
151
-	/**
152
-	 * Share a path
153
-	 *
154
-	 * @param IShare $share
155
-	 * @return IShare The share object
156
-	 * @throws ShareNotFound
157
-	 * @throws \Exception
158
-	 */
159
-	public function create(IShare $share) {
160
-
161
-		$shareWith = $share->getSharedWith();
162
-		/*
56
+    /** @var  IDBConnection */
57
+    private $dbConnection;
58
+
59
+    /** @var ILogger */
60
+    private $logger;
61
+
62
+    /** @var ISecureRandom */
63
+    private $secureRandom;
64
+
65
+    /** @var IUserManager */
66
+    private $userManager;
67
+
68
+    /** @var IRootFolder */
69
+    private $rootFolder;
70
+
71
+    /** @var IL10N */
72
+    private $l;
73
+
74
+    /** @var IMailer */
75
+    private $mailer;
76
+
77
+    /** @var IURLGenerator */
78
+    private $urlGenerator;
79
+
80
+    /** @var IManager  */
81
+    private $activityManager;
82
+
83
+    /** @var SettingsManager */
84
+    private $settingsManager;
85
+
86
+    /** @var Defaults */
87
+    private $defaults;
88
+
89
+    /** @var IHasher */
90
+    private $hasher;
91
+
92
+    /** @var  CapabilitiesManager */
93
+    private $capabilitiesManager;
94
+
95
+    /**
96
+     * Return the identifier of this provider.
97
+     *
98
+     * @return string Containing only [a-zA-Z0-9]
99
+     */
100
+    public function identifier() {
101
+        return 'ocMailShare';
102
+    }
103
+
104
+    /**
105
+     * DefaultShareProvider constructor.
106
+     *
107
+     * @param IDBConnection $connection
108
+     * @param ISecureRandom $secureRandom
109
+     * @param IUserManager $userManager
110
+     * @param IRootFolder $rootFolder
111
+     * @param IL10N $l
112
+     * @param ILogger $logger
113
+     * @param IMailer $mailer
114
+     * @param IURLGenerator $urlGenerator
115
+     * @param IManager $activityManager
116
+     * @param SettingsManager $settingsManager
117
+     * @param Defaults $defaults
118
+     * @param IHasher $hasher
119
+     * @param CapabilitiesManager $capabilitiesManager
120
+     */
121
+    public function __construct(
122
+        IDBConnection $connection,
123
+        ISecureRandom $secureRandom,
124
+        IUserManager $userManager,
125
+        IRootFolder $rootFolder,
126
+        IL10N $l,
127
+        ILogger $logger,
128
+        IMailer $mailer,
129
+        IURLGenerator $urlGenerator,
130
+        IManager $activityManager,
131
+        SettingsManager $settingsManager,
132
+        Defaults $defaults,
133
+        IHasher $hasher,
134
+        CapabilitiesManager $capabilitiesManager
135
+    ) {
136
+        $this->dbConnection = $connection;
137
+        $this->secureRandom = $secureRandom;
138
+        $this->userManager = $userManager;
139
+        $this->rootFolder = $rootFolder;
140
+        $this->l = $l;
141
+        $this->logger = $logger;
142
+        $this->mailer = $mailer;
143
+        $this->urlGenerator = $urlGenerator;
144
+        $this->activityManager = $activityManager;
145
+        $this->settingsManager = $settingsManager;
146
+        $this->defaults = $defaults;
147
+        $this->hasher = $hasher;
148
+        $this->capabilitiesManager = $capabilitiesManager;
149
+    }
150
+
151
+    /**
152
+     * Share a path
153
+     *
154
+     * @param IShare $share
155
+     * @return IShare The share object
156
+     * @throws ShareNotFound
157
+     * @throws \Exception
158
+     */
159
+    public function create(IShare $share) {
160
+
161
+        $shareWith = $share->getSharedWith();
162
+        /*
163 163
 		 * Check if file is not already shared with the remote user
164 164
 		 */
165
-		$alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0);
166
-		if (!empty($alreadyShared)) {
167
-			$message = 'Sharing %s failed, this item is already shared with %s';
168
-			$message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
169
-			$this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
170
-			throw new \Exception($message_t);
171
-		}
172
-
173
-		// if the admin enforces a password for all mail shares we create a
174
-		// random password and send it to the recipient
175
-		$password = '';
176
-		$passwordEnforced = $this->settingsManager->enforcePasswordProtection();
177
-		if ($passwordEnforced) {
178
-			$password = $this->autoGeneratePassword($share);
179
-		}
180
-
181
-		$shareId = $this->createMailShare($share);
182
-		$send = $this->sendPassword($share, $password);
183
-		if ($passwordEnforced && $send === false) {
184
-			$this->sendPasswordToOwner($share, $password);
185
-		}
186
-
187
-		$this->createShareActivity($share);
188
-		$data = $this->getRawShare($shareId);
189
-
190
-		return $this->createShareObject($data);
191
-
192
-	}
193
-
194
-	/**
195
-	 * auto generate password in case of password enforcement on mail shares
196
-	 *
197
-	 * @param IShare $share
198
-	 * @return string
199
-	 * @throws \Exception
200
-	 */
201
-	protected function autoGeneratePassword($share) {
202
-		$initiatorUser = $this->userManager->get($share->getSharedBy());
203
-		$initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
204
-		$allowPasswordByMail = $this->settingsManager->sendPasswordByMail();
205
-
206
-		if ($initiatorEMailAddress === null && !$allowPasswordByMail) {
207
-			throw new \Exception(
208
-				$this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.")
209
-			);
210
-		}
211
-
212
-		$passwordPolicy = $this->getPasswordPolicy();
213
-		$passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS;
214
-		$passwordLength = 8;
215
-		if (!empty($passwordPolicy)) {
216
-			$passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength;
217
-			$passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : '';
218
-		}
219
-
220
-		$password = $this->secureRandom->generate($passwordLength, $passwordCharset);
221
-
222
-		$share->setPassword($this->hasher->hash($password));
223
-
224
-		return $password;
225
-	}
226
-
227
-	/**
228
-	 * get password policy
229
-	 *
230
-	 * @return array
231
-	 */
232
-	protected function getPasswordPolicy() {
233
-		$capabilities = $this->capabilitiesManager->getCapabilities();
234
-		if (isset($capabilities['password_policy'])) {
235
-			return $capabilities['password_policy'];
236
-		}
237
-
238
-		return [];
239
-	}
240
-
241
-	/**
242
-	 * create activity if a file/folder was shared by mail
243
-	 *
244
-	 * @param IShare $share
245
-	 */
246
-	protected function createShareActivity(IShare $share) {
247
-
248
-		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
249
-
250
-		$this->publishActivity(
251
-			Activity::SUBJECT_SHARED_EMAIL_SELF,
252
-			[$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()],
253
-			$share->getSharedBy(),
254
-			$share->getNode()->getId(),
255
-			$userFolder->getRelativePath($share->getNode()->getPath())
256
-		);
257
-
258
-		if ($share->getShareOwner() !== $share->getSharedBy()) {
259
-			$ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
260
-			$fileId = $share->getNode()->getId();
261
-			$nodes = $ownerFolder->getById($fileId);
262
-			$ownerPath = $nodes[0]->getPath();
263
-			$this->publishActivity(
264
-				Activity::SUBJECT_SHARED_EMAIL_BY,
265
-				[$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()],
266
-				$share->getShareOwner(),
267
-				$fileId,
268
-				$ownerFolder->getRelativePath($ownerPath)
269
-			);
270
-		}
271
-
272
-	}
273
-
274
-	/**
275
-	 * create activity if a file/folder was shared by mail
276
-	 *
277
-	 * @param IShare $share
278
-	 * @param string $sharedWith
279
-	 * @param bool $sendToSelf
280
-	 */
281
-	protected function createPasswordSendActivity(IShare $share, $sharedWith, $sendToSelf) {
282
-
283
-		$userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
284
-
285
-		if ($sendToSelf) {
286
-			$this->publishActivity(
287
-				Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF,
288
-				[$userFolder->getRelativePath($share->getNode()->getPath())],
289
-				$share->getSharedBy(),
290
-				$share->getNode()->getId(),
291
-				$userFolder->getRelativePath($share->getNode()->getPath())
292
-			);
293
-		} else {
294
-			$this->publishActivity(
295
-				Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND,
296
-				[$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith],
297
-				$share->getSharedBy(),
298
-				$share->getNode()->getId(),
299
-				$userFolder->getRelativePath($share->getNode()->getPath())
300
-			);
301
-		}
302
-	}
303
-
304
-
305
-	/**
306
-	 * publish activity if a file/folder was shared by mail
307
-	 *
308
-	 * @param $subject
309
-	 * @param $parameters
310
-	 * @param $affectedUser
311
-	 * @param $fileId
312
-	 * @param $filePath
313
-	 */
314
-	protected function publishActivity($subject, $parameters, $affectedUser, $fileId, $filePath) {
315
-		$event = $this->activityManager->generateEvent();
316
-		$event->setApp('sharebymail')
317
-			->setType('shared')
318
-			->setSubject($subject, $parameters)
319
-			->setAffectedUser($affectedUser)
320
-			->setObject('files', $fileId, $filePath);
321
-		$this->activityManager->publish($event);
322
-
323
-	}
324
-
325
-	/**
326
-	 * @param IShare $share
327
-	 * @return int
328
-	 * @throws \Exception
329
-	 */
330
-	protected function createMailShare(IShare $share) {
331
-		$share->setToken($this->generateToken());
332
-		$shareId = $this->addShareToDB(
333
-			$share->getNodeId(),
334
-			$share->getNodeType(),
335
-			$share->getSharedWith(),
336
-			$share->getSharedBy(),
337
-			$share->getShareOwner(),
338
-			$share->getPermissions(),
339
-			$share->getToken(),
340
-			$share->getPassword()
341
-		);
342
-
343
-		try {
344
-			$link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare',
345
-				['token' => $share->getToken()]);
346
-			$this->sendMailNotification(
347
-				$share->getNode()->getName(),
348
-				$link,
349
-				$share->getSharedBy(),
350
-				$share->getSharedWith(),
351
-				$share->getExpirationDate()
352
-			);
353
-		} catch (HintException $hintException) {
354
-			$this->logger->logException($hintException, [
355
-				'message' => 'Failed to send share by mail.',
356
-				'level' => ILogger::ERROR,
357
-				'app' => 'sharebymail',
358
-			]);
359
-			$this->removeShareFromTable($shareId);
360
-			throw $hintException;
361
-		} catch (\Exception $e) {
362
-			$this->logger->logException($e, [
363
-				'message' => 'Failed to send share by mail.',
364
-				'level' => ILogger::ERROR,
365
-				'app' => 'sharebymail',
366
-			]);
367
-			$this->removeShareFromTable($shareId);
368
-			throw new HintException('Failed to send share by mail',
369
-				$this->l->t('Failed to send share by email'));
370
-		}
371
-
372
-		return $shareId;
373
-
374
-	}
375
-
376
-	/**
377
-	 * @param string $filename
378
-	 * @param string $link
379
-	 * @param string $initiator
380
-	 * @param string $shareWith
381
-	 * @param \DateTime|null $expiration
382
-	 * @throws \Exception If mail couldn't be sent
383
-	 */
384
-	protected function sendMailNotification($filename,
385
-											$link,
386
-											$initiator,
387
-											$shareWith,
388
-											\DateTime $expiration = null) {
389
-		$initiatorUser = $this->userManager->get($initiator);
390
-		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
391
-		$message = $this->mailer->createMessage();
392
-
393
-		$emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientNotification', [
394
-			'filename' => $filename,
395
-			'link' => $link,
396
-			'initiator' => $initiatorDisplayName,
397
-			'expiration' => $expiration,
398
-			'shareWith' => $shareWith,
399
-		]);
400
-
401
-		$emailTemplate->setSubject($this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename)));
402
-		$emailTemplate->addHeader();
403
-		$emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false);
404
-		$text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]);
405
-
406
-		$emailTemplate->addBodyText(
407
-			htmlspecialchars($text . ' ' . $this->l->t('Click the button below to open it.')),
408
-			$text
409
-		);
410
-		$emailTemplate->addBodyButton(
411
-			$this->l->t('Open »%s«', [$filename]),
412
-			$link
413
-		);
414
-
415
-		$message->setTo([$shareWith]);
416
-
417
-		// The "From" contains the sharers name
418
-		$instanceName = $this->defaults->getName();
419
-		$senderName = $this->l->t(
420
-			'%s via %s',
421
-			[
422
-				$initiatorDisplayName,
423
-				$instanceName
424
-			]
425
-		);
426
-		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
427
-
428
-		// The "Reply-To" is set to the sharer if an mail address is configured
429
-		// also the default footer contains a "Do not reply" which needs to be adjusted.
430
-		$initiatorEmail = $initiatorUser->getEMailAddress();
431
-		if($initiatorEmail !== null) {
432
-			$message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
433
-			$emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : ''));
434
-		} else {
435
-			$emailTemplate->addFooter();
436
-		}
437
-
438
-		$message->useTemplate($emailTemplate);
439
-		$this->mailer->send($message);
440
-	}
441
-
442
-	/**
443
-	 * send password to recipient of a mail share
444
-	 *
445
-	 * @param IShare $share
446
-	 * @param string $password
447
-	 * @return bool
448
-	 */
449
-	protected function sendPassword(IShare $share, $password) {
450
-
451
-		$filename = $share->getNode()->getName();
452
-		$initiator = $share->getSharedBy();
453
-		$shareWith = $share->getSharedWith();
454
-
455
-		if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) {
456
-			return false;
457
-		}
458
-
459
-		$initiatorUser = $this->userManager->get($initiator);
460
-		$initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
461
-		$initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
462
-
463
-		$plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]);
464
-		$htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]);
465
-
466
-		$message = $this->mailer->createMessage();
467
-
468
-		$emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientPasswordNotification', [
469
-			'filename' => $filename,
470
-			'password' => $password,
471
-			'initiator' => $initiatorDisplayName,
472
-			'initiatorEmail' => $initiatorEmailAddress,
473
-			'shareWith' => $shareWith,
474
-		]);
475
-
476
-		$emailTemplate->setSubject($this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]));
477
-		$emailTemplate->addHeader();
478
-		$emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
479
-		$emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart);
480
-		$emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password]));
481
-
482
-		// The "From" contains the sharers name
483
-		$instanceName = $this->defaults->getName();
484
-		$senderName = $this->l->t(
485
-			'%s via %s',
486
-			[
487
-				$initiatorDisplayName,
488
-				$instanceName
489
-			]
490
-		);
491
-		$message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
492
-		if ($initiatorEmailAddress !== null) {
493
-			$message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
494
-			$emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
495
-		} else {
496
-			$emailTemplate->addFooter();
497
-		}
498
-
499
-		$message->setTo([$shareWith]);
500
-		$message->useTemplate($emailTemplate);
501
-		$this->mailer->send($message);
502
-
503
-		$this->createPasswordSendActivity($share, $shareWith, false);
504
-
505
-		return true;
506
-	}
507
-
508
-	/**
509
-	 * send auto generated password to the owner. This happens if the admin enforces
510
-	 * a password for mail shares and forbid to send the password by mail to the recipient
511
-	 *
512
-	 * @param IShare $share
513
-	 * @param string $password
514
-	 * @return bool
515
-	 * @throws \Exception
516
-	 */
517
-	protected function sendPasswordToOwner(IShare $share, $password) {
518
-
519
-		$filename = $share->getNode()->getName();
520
-		$initiator = $this->userManager->get($share->getSharedBy());
521
-		$initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null;
522
-		$initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy();
523
-		$shareWith = $share->getSharedWith();
524
-
525
-		if ($initiatorEMailAddress === null) {
526
-			throw new \Exception(
527
-				$this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.")
528
-			);
529
-		}
530
-
531
-		$bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.", [$filename, $shareWith, $this->defaults->getName()]);
532
-
533
-		$message = $this->mailer->createMessage();
534
-		$emailTemplate = $this->mailer->createEMailTemplate('sharebymail.OwnerPasswordNotification', [
535
-			'filename' => $filename,
536
-			'password' => $password,
537
-			'initiator' => $initiatorDisplayName,
538
-			'initiatorEmail' => $initiatorEMailAddress,
539
-			'shareWith' => $shareWith,
540
-		]);
541
-
542
-		$emailTemplate->setSubject($this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]));
543
-		$emailTemplate->addHeader();
544
-		$emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
545
-		$emailTemplate->addBodyText($bodyPart);
546
-		$emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password]));
547
-		$emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.'));
548
-		$emailTemplate->addFooter();
549
-
550
-		if ($initiatorEMailAddress) {
551
-			$message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]);
552
-		}
553
-		$message->setTo([$initiatorEMailAddress => $initiatorDisplayName]);
554
-		$message->useTemplate($emailTemplate);
555
-		$this->mailer->send($message);
556
-
557
-		$this->createPasswordSendActivity($share, $shareWith, true);
558
-
559
-		return true;
560
-	}
561
-
562
-	/**
563
-	 * generate share token
564
-	 *
565
-	 * @return string
566
-	 */
567
-	protected function generateToken($size = 15) {
568
-		$token = $this->secureRandom->generate($size, ISecureRandom::CHAR_HUMAN_READABLE);
569
-		return $token;
570
-	}
571
-
572
-	/**
573
-	 * Get all children of this share
574
-	 *
575
-	 * @param IShare $parent
576
-	 * @return IShare[]
577
-	 */
578
-	public function getChildren(IShare $parent) {
579
-		$children = [];
580
-
581
-		$qb = $this->dbConnection->getQueryBuilder();
582
-		$qb->select('*')
583
-			->from('share')
584
-			->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
585
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
586
-			->orderBy('id');
587
-
588
-		$cursor = $qb->execute();
589
-		while($data = $cursor->fetch()) {
590
-			$children[] = $this->createShareObject($data);
591
-		}
592
-		$cursor->closeCursor();
593
-
594
-		return $children;
595
-	}
596
-
597
-	/**
598
-	 * add share to the database and return the ID
599
-	 *
600
-	 * @param int $itemSource
601
-	 * @param string $itemType
602
-	 * @param string $shareWith
603
-	 * @param string $sharedBy
604
-	 * @param string $uidOwner
605
-	 * @param int $permissions
606
-	 * @param string $token
607
-	 * @return int
608
-	 */
609
-	protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) {
610
-		$qb = $this->dbConnection->getQueryBuilder();
611
-		$qb->insert('share')
612
-			->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
613
-			->setValue('item_type', $qb->createNamedParameter($itemType))
614
-			->setValue('item_source', $qb->createNamedParameter($itemSource))
615
-			->setValue('file_source', $qb->createNamedParameter($itemSource))
616
-			->setValue('share_with', $qb->createNamedParameter($shareWith))
617
-			->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
618
-			->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
619
-			->setValue('permissions', $qb->createNamedParameter($permissions))
620
-			->setValue('token', $qb->createNamedParameter($token))
621
-			->setValue('password', $qb->createNamedParameter($password))
622
-			->setValue('stime', $qb->createNamedParameter(time()));
623
-
624
-		/*
165
+        $alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0);
166
+        if (!empty($alreadyShared)) {
167
+            $message = 'Sharing %s failed, this item is already shared with %s';
168
+            $message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith));
169
+            $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']);
170
+            throw new \Exception($message_t);
171
+        }
172
+
173
+        // if the admin enforces a password for all mail shares we create a
174
+        // random password and send it to the recipient
175
+        $password = '';
176
+        $passwordEnforced = $this->settingsManager->enforcePasswordProtection();
177
+        if ($passwordEnforced) {
178
+            $password = $this->autoGeneratePassword($share);
179
+        }
180
+
181
+        $shareId = $this->createMailShare($share);
182
+        $send = $this->sendPassword($share, $password);
183
+        if ($passwordEnforced && $send === false) {
184
+            $this->sendPasswordToOwner($share, $password);
185
+        }
186
+
187
+        $this->createShareActivity($share);
188
+        $data = $this->getRawShare($shareId);
189
+
190
+        return $this->createShareObject($data);
191
+
192
+    }
193
+
194
+    /**
195
+     * auto generate password in case of password enforcement on mail shares
196
+     *
197
+     * @param IShare $share
198
+     * @return string
199
+     * @throws \Exception
200
+     */
201
+    protected function autoGeneratePassword($share) {
202
+        $initiatorUser = $this->userManager->get($share->getSharedBy());
203
+        $initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
204
+        $allowPasswordByMail = $this->settingsManager->sendPasswordByMail();
205
+
206
+        if ($initiatorEMailAddress === null && !$allowPasswordByMail) {
207
+            throw new \Exception(
208
+                $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.")
209
+            );
210
+        }
211
+
212
+        $passwordPolicy = $this->getPasswordPolicy();
213
+        $passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS;
214
+        $passwordLength = 8;
215
+        if (!empty($passwordPolicy)) {
216
+            $passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength;
217
+            $passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : '';
218
+        }
219
+
220
+        $password = $this->secureRandom->generate($passwordLength, $passwordCharset);
221
+
222
+        $share->setPassword($this->hasher->hash($password));
223
+
224
+        return $password;
225
+    }
226
+
227
+    /**
228
+     * get password policy
229
+     *
230
+     * @return array
231
+     */
232
+    protected function getPasswordPolicy() {
233
+        $capabilities = $this->capabilitiesManager->getCapabilities();
234
+        if (isset($capabilities['password_policy'])) {
235
+            return $capabilities['password_policy'];
236
+        }
237
+
238
+        return [];
239
+    }
240
+
241
+    /**
242
+     * create activity if a file/folder was shared by mail
243
+     *
244
+     * @param IShare $share
245
+     */
246
+    protected function createShareActivity(IShare $share) {
247
+
248
+        $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
249
+
250
+        $this->publishActivity(
251
+            Activity::SUBJECT_SHARED_EMAIL_SELF,
252
+            [$userFolder->getRelativePath($share->getNode()->getPath()), $share->getSharedWith()],
253
+            $share->getSharedBy(),
254
+            $share->getNode()->getId(),
255
+            $userFolder->getRelativePath($share->getNode()->getPath())
256
+        );
257
+
258
+        if ($share->getShareOwner() !== $share->getSharedBy()) {
259
+            $ownerFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
260
+            $fileId = $share->getNode()->getId();
261
+            $nodes = $ownerFolder->getById($fileId);
262
+            $ownerPath = $nodes[0]->getPath();
263
+            $this->publishActivity(
264
+                Activity::SUBJECT_SHARED_EMAIL_BY,
265
+                [$ownerFolder->getRelativePath($ownerPath), $share->getSharedWith(), $share->getSharedBy()],
266
+                $share->getShareOwner(),
267
+                $fileId,
268
+                $ownerFolder->getRelativePath($ownerPath)
269
+            );
270
+        }
271
+
272
+    }
273
+
274
+    /**
275
+     * create activity if a file/folder was shared by mail
276
+     *
277
+     * @param IShare $share
278
+     * @param string $sharedWith
279
+     * @param bool $sendToSelf
280
+     */
281
+    protected function createPasswordSendActivity(IShare $share, $sharedWith, $sendToSelf) {
282
+
283
+        $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy());
284
+
285
+        if ($sendToSelf) {
286
+            $this->publishActivity(
287
+                Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF,
288
+                [$userFolder->getRelativePath($share->getNode()->getPath())],
289
+                $share->getSharedBy(),
290
+                $share->getNode()->getId(),
291
+                $userFolder->getRelativePath($share->getNode()->getPath())
292
+            );
293
+        } else {
294
+            $this->publishActivity(
295
+                Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND,
296
+                [$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith],
297
+                $share->getSharedBy(),
298
+                $share->getNode()->getId(),
299
+                $userFolder->getRelativePath($share->getNode()->getPath())
300
+            );
301
+        }
302
+    }
303
+
304
+
305
+    /**
306
+     * publish activity if a file/folder was shared by mail
307
+     *
308
+     * @param $subject
309
+     * @param $parameters
310
+     * @param $affectedUser
311
+     * @param $fileId
312
+     * @param $filePath
313
+     */
314
+    protected function publishActivity($subject, $parameters, $affectedUser, $fileId, $filePath) {
315
+        $event = $this->activityManager->generateEvent();
316
+        $event->setApp('sharebymail')
317
+            ->setType('shared')
318
+            ->setSubject($subject, $parameters)
319
+            ->setAffectedUser($affectedUser)
320
+            ->setObject('files', $fileId, $filePath);
321
+        $this->activityManager->publish($event);
322
+
323
+    }
324
+
325
+    /**
326
+     * @param IShare $share
327
+     * @return int
328
+     * @throws \Exception
329
+     */
330
+    protected function createMailShare(IShare $share) {
331
+        $share->setToken($this->generateToken());
332
+        $shareId = $this->addShareToDB(
333
+            $share->getNodeId(),
334
+            $share->getNodeType(),
335
+            $share->getSharedWith(),
336
+            $share->getSharedBy(),
337
+            $share->getShareOwner(),
338
+            $share->getPermissions(),
339
+            $share->getToken(),
340
+            $share->getPassword()
341
+        );
342
+
343
+        try {
344
+            $link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare',
345
+                ['token' => $share->getToken()]);
346
+            $this->sendMailNotification(
347
+                $share->getNode()->getName(),
348
+                $link,
349
+                $share->getSharedBy(),
350
+                $share->getSharedWith(),
351
+                $share->getExpirationDate()
352
+            );
353
+        } catch (HintException $hintException) {
354
+            $this->logger->logException($hintException, [
355
+                'message' => 'Failed to send share by mail.',
356
+                'level' => ILogger::ERROR,
357
+                'app' => 'sharebymail',
358
+            ]);
359
+            $this->removeShareFromTable($shareId);
360
+            throw $hintException;
361
+        } catch (\Exception $e) {
362
+            $this->logger->logException($e, [
363
+                'message' => 'Failed to send share by mail.',
364
+                'level' => ILogger::ERROR,
365
+                'app' => 'sharebymail',
366
+            ]);
367
+            $this->removeShareFromTable($shareId);
368
+            throw new HintException('Failed to send share by mail',
369
+                $this->l->t('Failed to send share by email'));
370
+        }
371
+
372
+        return $shareId;
373
+
374
+    }
375
+
376
+    /**
377
+     * @param string $filename
378
+     * @param string $link
379
+     * @param string $initiator
380
+     * @param string $shareWith
381
+     * @param \DateTime|null $expiration
382
+     * @throws \Exception If mail couldn't be sent
383
+     */
384
+    protected function sendMailNotification($filename,
385
+                                            $link,
386
+                                            $initiator,
387
+                                            $shareWith,
388
+                                            \DateTime $expiration = null) {
389
+        $initiatorUser = $this->userManager->get($initiator);
390
+        $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
391
+        $message = $this->mailer->createMessage();
392
+
393
+        $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientNotification', [
394
+            'filename' => $filename,
395
+            'link' => $link,
396
+            'initiator' => $initiatorDisplayName,
397
+            'expiration' => $expiration,
398
+            'shareWith' => $shareWith,
399
+        ]);
400
+
401
+        $emailTemplate->setSubject($this->l->t('%s shared »%s« with you', array($initiatorDisplayName, $filename)));
402
+        $emailTemplate->addHeader();
403
+        $emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$initiatorDisplayName, $filename]), false);
404
+        $text = $this->l->t('%s shared »%s« with you.', [$initiatorDisplayName, $filename]);
405
+
406
+        $emailTemplate->addBodyText(
407
+            htmlspecialchars($text . ' ' . $this->l->t('Click the button below to open it.')),
408
+            $text
409
+        );
410
+        $emailTemplate->addBodyButton(
411
+            $this->l->t('Open »%s«', [$filename]),
412
+            $link
413
+        );
414
+
415
+        $message->setTo([$shareWith]);
416
+
417
+        // The "From" contains the sharers name
418
+        $instanceName = $this->defaults->getName();
419
+        $senderName = $this->l->t(
420
+            '%s via %s',
421
+            [
422
+                $initiatorDisplayName,
423
+                $instanceName
424
+            ]
425
+        );
426
+        $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
427
+
428
+        // The "Reply-To" is set to the sharer if an mail address is configured
429
+        // also the default footer contains a "Do not reply" which needs to be adjusted.
430
+        $initiatorEmail = $initiatorUser->getEMailAddress();
431
+        if($initiatorEmail !== null) {
432
+            $message->setReplyTo([$initiatorEmail => $initiatorDisplayName]);
433
+            $emailTemplate->addFooter($instanceName . ($this->defaults->getSlogan() !== '' ? ' - ' . $this->defaults->getSlogan() : ''));
434
+        } else {
435
+            $emailTemplate->addFooter();
436
+        }
437
+
438
+        $message->useTemplate($emailTemplate);
439
+        $this->mailer->send($message);
440
+    }
441
+
442
+    /**
443
+     * send password to recipient of a mail share
444
+     *
445
+     * @param IShare $share
446
+     * @param string $password
447
+     * @return bool
448
+     */
449
+    protected function sendPassword(IShare $share, $password) {
450
+
451
+        $filename = $share->getNode()->getName();
452
+        $initiator = $share->getSharedBy();
453
+        $shareWith = $share->getSharedWith();
454
+
455
+        if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) {
456
+            return false;
457
+        }
458
+
459
+        $initiatorUser = $this->userManager->get($initiator);
460
+        $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator;
461
+        $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null;
462
+
463
+        $plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]);
464
+        $htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]);
465
+
466
+        $message = $this->mailer->createMessage();
467
+
468
+        $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.RecipientPasswordNotification', [
469
+            'filename' => $filename,
470
+            'password' => $password,
471
+            'initiator' => $initiatorDisplayName,
472
+            'initiatorEmail' => $initiatorEmailAddress,
473
+            'shareWith' => $shareWith,
474
+        ]);
475
+
476
+        $emailTemplate->setSubject($this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]));
477
+        $emailTemplate->addHeader();
478
+        $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
479
+        $emailTemplate->addBodyText(htmlspecialchars($htmlBodyPart), $plainBodyPart);
480
+        $emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password]));
481
+
482
+        // The "From" contains the sharers name
483
+        $instanceName = $this->defaults->getName();
484
+        $senderName = $this->l->t(
485
+            '%s via %s',
486
+            [
487
+                $initiatorDisplayName,
488
+                $instanceName
489
+            ]
490
+        );
491
+        $message->setFrom([\OCP\Util::getDefaultEmailAddress($instanceName) => $senderName]);
492
+        if ($initiatorEmailAddress !== null) {
493
+            $message->setReplyTo([$initiatorEmailAddress => $initiatorDisplayName]);
494
+            $emailTemplate->addFooter($instanceName . ' - ' . $this->defaults->getSlogan());
495
+        } else {
496
+            $emailTemplate->addFooter();
497
+        }
498
+
499
+        $message->setTo([$shareWith]);
500
+        $message->useTemplate($emailTemplate);
501
+        $this->mailer->send($message);
502
+
503
+        $this->createPasswordSendActivity($share, $shareWith, false);
504
+
505
+        return true;
506
+    }
507
+
508
+    /**
509
+     * send auto generated password to the owner. This happens if the admin enforces
510
+     * a password for mail shares and forbid to send the password by mail to the recipient
511
+     *
512
+     * @param IShare $share
513
+     * @param string $password
514
+     * @return bool
515
+     * @throws \Exception
516
+     */
517
+    protected function sendPasswordToOwner(IShare $share, $password) {
518
+
519
+        $filename = $share->getNode()->getName();
520
+        $initiator = $this->userManager->get($share->getSharedBy());
521
+        $initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null;
522
+        $initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy();
523
+        $shareWith = $share->getSharedWith();
524
+
525
+        if ($initiatorEMailAddress === null) {
526
+            throw new \Exception(
527
+                $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.")
528
+            );
529
+        }
530
+
531
+        $bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.", [$filename, $shareWith, $this->defaults->getName()]);
532
+
533
+        $message = $this->mailer->createMessage();
534
+        $emailTemplate = $this->mailer->createEMailTemplate('sharebymail.OwnerPasswordNotification', [
535
+            'filename' => $filename,
536
+            'password' => $password,
537
+            'initiator' => $initiatorDisplayName,
538
+            'initiatorEmail' => $initiatorEMailAddress,
539
+            'shareWith' => $shareWith,
540
+        ]);
541
+
542
+        $emailTemplate->setSubject($this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]));
543
+        $emailTemplate->addHeader();
544
+        $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false);
545
+        $emailTemplate->addBodyText($bodyPart);
546
+        $emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password]));
547
+        $emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.'));
548
+        $emailTemplate->addFooter();
549
+
550
+        if ($initiatorEMailAddress) {
551
+            $message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]);
552
+        }
553
+        $message->setTo([$initiatorEMailAddress => $initiatorDisplayName]);
554
+        $message->useTemplate($emailTemplate);
555
+        $this->mailer->send($message);
556
+
557
+        $this->createPasswordSendActivity($share, $shareWith, true);
558
+
559
+        return true;
560
+    }
561
+
562
+    /**
563
+     * generate share token
564
+     *
565
+     * @return string
566
+     */
567
+    protected function generateToken($size = 15) {
568
+        $token = $this->secureRandom->generate($size, ISecureRandom::CHAR_HUMAN_READABLE);
569
+        return $token;
570
+    }
571
+
572
+    /**
573
+     * Get all children of this share
574
+     *
575
+     * @param IShare $parent
576
+     * @return IShare[]
577
+     */
578
+    public function getChildren(IShare $parent) {
579
+        $children = [];
580
+
581
+        $qb = $this->dbConnection->getQueryBuilder();
582
+        $qb->select('*')
583
+            ->from('share')
584
+            ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId())))
585
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
586
+            ->orderBy('id');
587
+
588
+        $cursor = $qb->execute();
589
+        while($data = $cursor->fetch()) {
590
+            $children[] = $this->createShareObject($data);
591
+        }
592
+        $cursor->closeCursor();
593
+
594
+        return $children;
595
+    }
596
+
597
+    /**
598
+     * add share to the database and return the ID
599
+     *
600
+     * @param int $itemSource
601
+     * @param string $itemType
602
+     * @param string $shareWith
603
+     * @param string $sharedBy
604
+     * @param string $uidOwner
605
+     * @param int $permissions
606
+     * @param string $token
607
+     * @return int
608
+     */
609
+    protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) {
610
+        $qb = $this->dbConnection->getQueryBuilder();
611
+        $qb->insert('share')
612
+            ->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
613
+            ->setValue('item_type', $qb->createNamedParameter($itemType))
614
+            ->setValue('item_source', $qb->createNamedParameter($itemSource))
615
+            ->setValue('file_source', $qb->createNamedParameter($itemSource))
616
+            ->setValue('share_with', $qb->createNamedParameter($shareWith))
617
+            ->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
618
+            ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
619
+            ->setValue('permissions', $qb->createNamedParameter($permissions))
620
+            ->setValue('token', $qb->createNamedParameter($token))
621
+            ->setValue('password', $qb->createNamedParameter($password))
622
+            ->setValue('stime', $qb->createNamedParameter(time()));
623
+
624
+        /*
625 625
 		 * Added to fix https://github.com/owncloud/core/issues/22215
626 626
 		 * Can be removed once we get rid of ajax/share.php
627 627
 		 */
628
-		$qb->setValue('file_target', $qb->createNamedParameter(''));
628
+        $qb->setValue('file_target', $qb->createNamedParameter(''));
629 629
 
630
-		$qb->execute();
631
-		$id = $qb->getLastInsertId();
630
+        $qb->execute();
631
+        $id = $qb->getLastInsertId();
632 632
 
633
-		return (int)$id;
634
-	}
633
+        return (int)$id;
634
+    }
635 635
 
636
-	/**
637
-	 * Update a share
638
-	 *
639
-	 * @param IShare $share
640
-	 * @param string|null $plainTextPassword
641
-	 * @return IShare The share object
642
-	 */
643
-	public function update(IShare $share, $plainTextPassword = null) {
636
+    /**
637
+     * Update a share
638
+     *
639
+     * @param IShare $share
640
+     * @param string|null $plainTextPassword
641
+     * @return IShare The share object
642
+     */
643
+    public function update(IShare $share, $plainTextPassword = null) {
644 644
 
645
-		$originalShare = $this->getShareById($share->getId());
645
+        $originalShare = $this->getShareById($share->getId());
646 646
 
647
-		// a real password was given
648
-		$validPassword = $plainTextPassword !== null && $plainTextPassword !== '';
647
+        // a real password was given
648
+        $validPassword = $plainTextPassword !== null && $plainTextPassword !== '';
649 649
 
650
-		if($validPassword && $originalShare->getPassword() !== $share->getPassword()) {
651
-			$this->sendPassword($share, $plainTextPassword);
652
-		}
653
-		/*
650
+        if($validPassword && $originalShare->getPassword() !== $share->getPassword()) {
651
+            $this->sendPassword($share, $plainTextPassword);
652
+        }
653
+        /*
654 654
 		 * We allow updating the permissions and password of mail shares
655 655
 		 */
656
-		$qb = $this->dbConnection->getQueryBuilder();
657
-		$qb->update('share')
658
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
659
-			->set('permissions', $qb->createNamedParameter($share->getPermissions()))
660
-			->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
661
-			->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
662
-			->set('password', $qb->createNamedParameter($share->getPassword()))
663
-			->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
664
-			->execute();
665
-
666
-		return $share;
667
-	}
668
-
669
-	/**
670
-	 * @inheritdoc
671
-	 */
672
-	public function move(IShare $share, $recipient) {
673
-		/**
674
-		 * nothing to do here, mail shares are only outgoing shares
675
-		 */
676
-		return $share;
677
-	}
678
-
679
-	/**
680
-	 * Delete a share (owner unShares the file)
681
-	 *
682
-	 * @param IShare $share
683
-	 */
684
-	public function delete(IShare $share) {
685
-		$this->removeShareFromTable($share->getId());
686
-	}
687
-
688
-	/**
689
-	 * @inheritdoc
690
-	 */
691
-	public function deleteFromSelf(IShare $share, $recipient) {
692
-		// nothing to do here, mail shares are only outgoing shares
693
-	}
694
-
695
-	/**
696
-	 * @inheritdoc
697
-	 */
698
-	public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
699
-		$qb = $this->dbConnection->getQueryBuilder();
700
-		$qb->select('*')
701
-			->from('share');
702
-
703
-		$qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
704
-
705
-		/**
706
-		 * Reshares for this user are shares where they are the owner.
707
-		 */
708
-		if ($reshares === false) {
709
-			//Special case for old shares created via the web UI
710
-			$or1 = $qb->expr()->andX(
711
-				$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
712
-				$qb->expr()->isNull('uid_initiator')
713
-			);
714
-
715
-			$qb->andWhere(
716
-				$qb->expr()->orX(
717
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
718
-					$or1
719
-				)
720
-			);
721
-		} else {
722
-			$qb->andWhere(
723
-				$qb->expr()->orX(
724
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
725
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
726
-				)
727
-			);
728
-		}
729
-
730
-		if ($node !== null) {
731
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
732
-		}
733
-
734
-		if ($limit !== -1) {
735
-			$qb->setMaxResults($limit);
736
-		}
737
-
738
-		$qb->setFirstResult($offset);
739
-		$qb->orderBy('id');
740
-
741
-		$cursor = $qb->execute();
742
-		$shares = [];
743
-		while($data = $cursor->fetch()) {
744
-			$shares[] = $this->createShareObject($data);
745
-		}
746
-		$cursor->closeCursor();
747
-
748
-		return $shares;
749
-	}
750
-
751
-	/**
752
-	 * @inheritdoc
753
-	 */
754
-	public function getShareById($id, $recipientId = null) {
755
-		$qb = $this->dbConnection->getQueryBuilder();
756
-
757
-		$qb->select('*')
758
-			->from('share')
759
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
760
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
761
-
762
-		$cursor = $qb->execute();
763
-		$data = $cursor->fetch();
764
-		$cursor->closeCursor();
765
-
766
-		if ($data === false) {
767
-			throw new ShareNotFound();
768
-		}
769
-
770
-		try {
771
-			$share = $this->createShareObject($data);
772
-		} catch (InvalidShare $e) {
773
-			throw new ShareNotFound();
774
-		}
775
-
776
-		return $share;
777
-	}
778
-
779
-	/**
780
-	 * Get shares for a given path
781
-	 *
782
-	 * @param \OCP\Files\Node $path
783
-	 * @return IShare[]
784
-	 */
785
-	public function getSharesByPath(Node $path) {
786
-		$qb = $this->dbConnection->getQueryBuilder();
787
-
788
-		$cursor = $qb->select('*')
789
-			->from('share')
790
-			->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
791
-			->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
792
-			->execute();
793
-
794
-		$shares = [];
795
-		while($data = $cursor->fetch()) {
796
-			$shares[] = $this->createShareObject($data);
797
-		}
798
-		$cursor->closeCursor();
799
-
800
-		return $shares;
801
-	}
802
-
803
-	/**
804
-	 * @inheritdoc
805
-	 */
806
-	public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
807
-		/** @var IShare[] $shares */
808
-		$shares = [];
809
-
810
-		//Get shares directly with this user
811
-		$qb = $this->dbConnection->getQueryBuilder();
812
-		$qb->select('*')
813
-			->from('share');
814
-
815
-		// Order by id
816
-		$qb->orderBy('id');
817
-
818
-		// Set limit and offset
819
-		if ($limit !== -1) {
820
-			$qb->setMaxResults($limit);
821
-		}
822
-		$qb->setFirstResult($offset);
823
-
824
-		$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
825
-		$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
826
-
827
-		// Filter by node if provided
828
-		if ($node !== null) {
829
-			$qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
830
-		}
831
-
832
-		$cursor = $qb->execute();
833
-
834
-		while($data = $cursor->fetch()) {
835
-			$shares[] = $this->createShareObject($data);
836
-		}
837
-		$cursor->closeCursor();
838
-
839
-
840
-		return $shares;
841
-	}
842
-
843
-	/**
844
-	 * Get a share by token
845
-	 *
846
-	 * @param string $token
847
-	 * @return IShare
848
-	 * @throws ShareNotFound
849
-	 */
850
-	public function getShareByToken($token) {
851
-		$qb = $this->dbConnection->getQueryBuilder();
852
-
853
-		$cursor = $qb->select('*')
854
-			->from('share')
855
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
856
-			->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
857
-			->execute();
858
-
859
-		$data = $cursor->fetch();
860
-
861
-		if ($data === false) {
862
-			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
863
-		}
864
-
865
-		try {
866
-			$share = $this->createShareObject($data);
867
-		} catch (InvalidShare $e) {
868
-			throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
869
-		}
870
-
871
-		return $share;
872
-	}
873
-
874
-	/**
875
-	 * remove share from table
876
-	 *
877
-	 * @param string $shareId
878
-	 */
879
-	protected function removeShareFromTable($shareId) {
880
-		$qb = $this->dbConnection->getQueryBuilder();
881
-		$qb->delete('share')
882
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
883
-		$qb->execute();
884
-	}
885
-
886
-	/**
887
-	 * Create a share object from an database row
888
-	 *
889
-	 * @param array $data
890
-	 * @return IShare
891
-	 * @throws InvalidShare
892
-	 * @throws ShareNotFound
893
-	 */
894
-	protected function createShareObject($data) {
895
-
896
-		$share = new Share($this->rootFolder, $this->userManager);
897
-		$share->setId((int)$data['id'])
898
-			->setShareType((int)$data['share_type'])
899
-			->setPermissions((int)$data['permissions'])
900
-			->setTarget($data['file_target'])
901
-			->setMailSend((bool)$data['mail_send'])
902
-			->setToken($data['token']);
903
-
904
-		$shareTime = new \DateTime();
905
-		$shareTime->setTimestamp((int)$data['stime']);
906
-		$share->setShareTime($shareTime);
907
-		$share->setSharedWith($data['share_with']);
908
-		$share->setPassword($data['password']);
909
-
910
-		if ($data['uid_initiator'] !== null) {
911
-			$share->setShareOwner($data['uid_owner']);
912
-			$share->setSharedBy($data['uid_initiator']);
913
-		} else {
914
-			//OLD SHARE
915
-			$share->setSharedBy($data['uid_owner']);
916
-			$path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
917
-
918
-			$owner = $path->getOwner();
919
-			$share->setShareOwner($owner->getUID());
920
-		}
921
-
922
-		if ($data['expiration'] !== null) {
923
-			$expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
924
-			if ($expiration !== false) {
925
-				$share->setExpirationDate($expiration);
926
-			}
927
-		}
928
-
929
-		$share->setNodeId((int)$data['file_source']);
930
-		$share->setNodeType($data['item_type']);
931
-
932
-		$share->setProviderId($this->identifier());
933
-
934
-		return $share;
935
-	}
936
-
937
-	/**
938
-	 * Get the node with file $id for $user
939
-	 *
940
-	 * @param string $userId
941
-	 * @param int $id
942
-	 * @return \OCP\Files\File|\OCP\Files\Folder
943
-	 * @throws InvalidShare
944
-	 */
945
-	private function getNode($userId, $id) {
946
-		try {
947
-			$userFolder = $this->rootFolder->getUserFolder($userId);
948
-		} catch (NoUserException $e) {
949
-			throw new InvalidShare();
950
-		}
951
-
952
-		$nodes = $userFolder->getById($id);
953
-
954
-		if (empty($nodes)) {
955
-			throw new InvalidShare();
956
-		}
957
-
958
-		return $nodes[0];
959
-	}
960
-
961
-	/**
962
-	 * A user is deleted from the system
963
-	 * So clean up the relevant shares.
964
-	 *
965
-	 * @param string $uid
966
-	 * @param int $shareType
967
-	 */
968
-	public function userDeleted($uid, $shareType) {
969
-		$qb = $this->dbConnection->getQueryBuilder();
970
-
971
-		$qb->delete('share')
972
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
973
-			->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
974
-			->execute();
975
-	}
976
-
977
-	/**
978
-	 * This provider does not support group shares
979
-	 *
980
-	 * @param string $gid
981
-	 */
982
-	public function groupDeleted($gid) {
983
-	}
984
-
985
-	/**
986
-	 * This provider does not support group shares
987
-	 *
988
-	 * @param string $uid
989
-	 * @param string $gid
990
-	 */
991
-	public function userDeletedFromGroup($uid, $gid) {
992
-	}
993
-
994
-	/**
995
-	 * get database row of a give share
996
-	 *
997
-	 * @param $id
998
-	 * @return array
999
-	 * @throws ShareNotFound
1000
-	 */
1001
-	protected function getRawShare($id) {
1002
-
1003
-		// Now fetch the inserted share and create a complete share object
1004
-		$qb = $this->dbConnection->getQueryBuilder();
1005
-		$qb->select('*')
1006
-			->from('share')
1007
-			->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
1008
-
1009
-		$cursor = $qb->execute();
1010
-		$data = $cursor->fetch();
1011
-		$cursor->closeCursor();
1012
-
1013
-		if ($data === false) {
1014
-			throw new ShareNotFound;
1015
-		}
1016
-
1017
-		return $data;
1018
-	}
1019
-
1020
-	public function getSharesInFolder($userId, Folder $node, $reshares) {
1021
-		$qb = $this->dbConnection->getQueryBuilder();
1022
-		$qb->select('*')
1023
-			->from('share', 's')
1024
-			->andWhere($qb->expr()->orX(
1025
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1026
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1027
-			))
1028
-			->andWhere(
1029
-				$qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
1030
-			);
1031
-
1032
-		/**
1033
-		 * Reshares for this user are shares where they are the owner.
1034
-		 */
1035
-		if ($reshares === false) {
1036
-			$qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
1037
-		} else {
1038
-			$qb->andWhere(
1039
-				$qb->expr()->orX(
1040
-					$qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
1041
-					$qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
1042
-				)
1043
-			);
1044
-		}
1045
-
1046
-		$qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
1047
-		$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
1048
-
1049
-		$qb->orderBy('id');
1050
-
1051
-		$cursor = $qb->execute();
1052
-		$shares = [];
1053
-		while ($data = $cursor->fetch()) {
1054
-			$shares[$data['fileid']][] = $this->createShareObject($data);
1055
-		}
1056
-		$cursor->closeCursor();
1057
-
1058
-		return $shares;
1059
-	}
1060
-
1061
-	/**
1062
-	 * @inheritdoc
1063
-	 */
1064
-	public function getAccessList($nodes, $currentAccess) {
1065
-		$ids = [];
1066
-		foreach ($nodes as $node) {
1067
-			$ids[] = $node->getId();
1068
-		}
1069
-
1070
-		$qb = $this->dbConnection->getQueryBuilder();
1071
-		$qb->select('share_with')
1072
-			->from('share')
1073
-			->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
1074
-			->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1075
-			->andWhere($qb->expr()->orX(
1076
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1077
-				$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1078
-			))
1079
-			->setMaxResults(1);
1080
-		$cursor = $qb->execute();
1081
-
1082
-		$mail = $cursor->fetch() !== false;
1083
-		$cursor->closeCursor();
1084
-
1085
-		return ['public' => $mail];
1086
-	}
656
+        $qb = $this->dbConnection->getQueryBuilder();
657
+        $qb->update('share')
658
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
659
+            ->set('permissions', $qb->createNamedParameter($share->getPermissions()))
660
+            ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner()))
661
+            ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy()))
662
+            ->set('password', $qb->createNamedParameter($share->getPassword()))
663
+            ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
664
+            ->execute();
665
+
666
+        return $share;
667
+    }
668
+
669
+    /**
670
+     * @inheritdoc
671
+     */
672
+    public function move(IShare $share, $recipient) {
673
+        /**
674
+         * nothing to do here, mail shares are only outgoing shares
675
+         */
676
+        return $share;
677
+    }
678
+
679
+    /**
680
+     * Delete a share (owner unShares the file)
681
+     *
682
+     * @param IShare $share
683
+     */
684
+    public function delete(IShare $share) {
685
+        $this->removeShareFromTable($share->getId());
686
+    }
687
+
688
+    /**
689
+     * @inheritdoc
690
+     */
691
+    public function deleteFromSelf(IShare $share, $recipient) {
692
+        // nothing to do here, mail shares are only outgoing shares
693
+    }
694
+
695
+    /**
696
+     * @inheritdoc
697
+     */
698
+    public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) {
699
+        $qb = $this->dbConnection->getQueryBuilder();
700
+        $qb->select('*')
701
+            ->from('share');
702
+
703
+        $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
704
+
705
+        /**
706
+         * Reshares for this user are shares where they are the owner.
707
+         */
708
+        if ($reshares === false) {
709
+            //Special case for old shares created via the web UI
710
+            $or1 = $qb->expr()->andX(
711
+                $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
712
+                $qb->expr()->isNull('uid_initiator')
713
+            );
714
+
715
+            $qb->andWhere(
716
+                $qb->expr()->orX(
717
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
718
+                    $or1
719
+                )
720
+            );
721
+        } else {
722
+            $qb->andWhere(
723
+                $qb->expr()->orX(
724
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
725
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
726
+                )
727
+            );
728
+        }
729
+
730
+        if ($node !== null) {
731
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
732
+        }
733
+
734
+        if ($limit !== -1) {
735
+            $qb->setMaxResults($limit);
736
+        }
737
+
738
+        $qb->setFirstResult($offset);
739
+        $qb->orderBy('id');
740
+
741
+        $cursor = $qb->execute();
742
+        $shares = [];
743
+        while($data = $cursor->fetch()) {
744
+            $shares[] = $this->createShareObject($data);
745
+        }
746
+        $cursor->closeCursor();
747
+
748
+        return $shares;
749
+    }
750
+
751
+    /**
752
+     * @inheritdoc
753
+     */
754
+    public function getShareById($id, $recipientId = null) {
755
+        $qb = $this->dbConnection->getQueryBuilder();
756
+
757
+        $qb->select('*')
758
+            ->from('share')
759
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
760
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
761
+
762
+        $cursor = $qb->execute();
763
+        $data = $cursor->fetch();
764
+        $cursor->closeCursor();
765
+
766
+        if ($data === false) {
767
+            throw new ShareNotFound();
768
+        }
769
+
770
+        try {
771
+            $share = $this->createShareObject($data);
772
+        } catch (InvalidShare $e) {
773
+            throw new ShareNotFound();
774
+        }
775
+
776
+        return $share;
777
+    }
778
+
779
+    /**
780
+     * Get shares for a given path
781
+     *
782
+     * @param \OCP\Files\Node $path
783
+     * @return IShare[]
784
+     */
785
+    public function getSharesByPath(Node $path) {
786
+        $qb = $this->dbConnection->getQueryBuilder();
787
+
788
+        $cursor = $qb->select('*')
789
+            ->from('share')
790
+            ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
791
+            ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
792
+            ->execute();
793
+
794
+        $shares = [];
795
+        while($data = $cursor->fetch()) {
796
+            $shares[] = $this->createShareObject($data);
797
+        }
798
+        $cursor->closeCursor();
799
+
800
+        return $shares;
801
+    }
802
+
803
+    /**
804
+     * @inheritdoc
805
+     */
806
+    public function getSharedWith($userId, $shareType, $node, $limit, $offset) {
807
+        /** @var IShare[] $shares */
808
+        $shares = [];
809
+
810
+        //Get shares directly with this user
811
+        $qb = $this->dbConnection->getQueryBuilder();
812
+        $qb->select('*')
813
+            ->from('share');
814
+
815
+        // Order by id
816
+        $qb->orderBy('id');
817
+
818
+        // Set limit and offset
819
+        if ($limit !== -1) {
820
+            $qb->setMaxResults($limit);
821
+        }
822
+        $qb->setFirstResult($offset);
823
+
824
+        $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)));
825
+        $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId)));
826
+
827
+        // Filter by node if provided
828
+        if ($node !== null) {
829
+            $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
830
+        }
831
+
832
+        $cursor = $qb->execute();
833
+
834
+        while($data = $cursor->fetch()) {
835
+            $shares[] = $this->createShareObject($data);
836
+        }
837
+        $cursor->closeCursor();
838
+
839
+
840
+        return $shares;
841
+    }
842
+
843
+    /**
844
+     * Get a share by token
845
+     *
846
+     * @param string $token
847
+     * @return IShare
848
+     * @throws ShareNotFound
849
+     */
850
+    public function getShareByToken($token) {
851
+        $qb = $this->dbConnection->getQueryBuilder();
852
+
853
+        $cursor = $qb->select('*')
854
+            ->from('share')
855
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
856
+            ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
857
+            ->execute();
858
+
859
+        $data = $cursor->fetch();
860
+
861
+        if ($data === false) {
862
+            throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
863
+        }
864
+
865
+        try {
866
+            $share = $this->createShareObject($data);
867
+        } catch (InvalidShare $e) {
868
+            throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
869
+        }
870
+
871
+        return $share;
872
+    }
873
+
874
+    /**
875
+     * remove share from table
876
+     *
877
+     * @param string $shareId
878
+     */
879
+    protected function removeShareFromTable($shareId) {
880
+        $qb = $this->dbConnection->getQueryBuilder();
881
+        $qb->delete('share')
882
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId)));
883
+        $qb->execute();
884
+    }
885
+
886
+    /**
887
+     * Create a share object from an database row
888
+     *
889
+     * @param array $data
890
+     * @return IShare
891
+     * @throws InvalidShare
892
+     * @throws ShareNotFound
893
+     */
894
+    protected function createShareObject($data) {
895
+
896
+        $share = new Share($this->rootFolder, $this->userManager);
897
+        $share->setId((int)$data['id'])
898
+            ->setShareType((int)$data['share_type'])
899
+            ->setPermissions((int)$data['permissions'])
900
+            ->setTarget($data['file_target'])
901
+            ->setMailSend((bool)$data['mail_send'])
902
+            ->setToken($data['token']);
903
+
904
+        $shareTime = new \DateTime();
905
+        $shareTime->setTimestamp((int)$data['stime']);
906
+        $share->setShareTime($shareTime);
907
+        $share->setSharedWith($data['share_with']);
908
+        $share->setPassword($data['password']);
909
+
910
+        if ($data['uid_initiator'] !== null) {
911
+            $share->setShareOwner($data['uid_owner']);
912
+            $share->setSharedBy($data['uid_initiator']);
913
+        } else {
914
+            //OLD SHARE
915
+            $share->setSharedBy($data['uid_owner']);
916
+            $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']);
917
+
918
+            $owner = $path->getOwner();
919
+            $share->setShareOwner($owner->getUID());
920
+        }
921
+
922
+        if ($data['expiration'] !== null) {
923
+            $expiration = \DateTime::createFromFormat('Y-m-d H:i:s', $data['expiration']);
924
+            if ($expiration !== false) {
925
+                $share->setExpirationDate($expiration);
926
+            }
927
+        }
928
+
929
+        $share->setNodeId((int)$data['file_source']);
930
+        $share->setNodeType($data['item_type']);
931
+
932
+        $share->setProviderId($this->identifier());
933
+
934
+        return $share;
935
+    }
936
+
937
+    /**
938
+     * Get the node with file $id for $user
939
+     *
940
+     * @param string $userId
941
+     * @param int $id
942
+     * @return \OCP\Files\File|\OCP\Files\Folder
943
+     * @throws InvalidShare
944
+     */
945
+    private function getNode($userId, $id) {
946
+        try {
947
+            $userFolder = $this->rootFolder->getUserFolder($userId);
948
+        } catch (NoUserException $e) {
949
+            throw new InvalidShare();
950
+        }
951
+
952
+        $nodes = $userFolder->getById($id);
953
+
954
+        if (empty($nodes)) {
955
+            throw new InvalidShare();
956
+        }
957
+
958
+        return $nodes[0];
959
+    }
960
+
961
+    /**
962
+     * A user is deleted from the system
963
+     * So clean up the relevant shares.
964
+     *
965
+     * @param string $uid
966
+     * @param int $shareType
967
+     */
968
+    public function userDeleted($uid, $shareType) {
969
+        $qb = $this->dbConnection->getQueryBuilder();
970
+
971
+        $qb->delete('share')
972
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
973
+            ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid)))
974
+            ->execute();
975
+    }
976
+
977
+    /**
978
+     * This provider does not support group shares
979
+     *
980
+     * @param string $gid
981
+     */
982
+    public function groupDeleted($gid) {
983
+    }
984
+
985
+    /**
986
+     * This provider does not support group shares
987
+     *
988
+     * @param string $uid
989
+     * @param string $gid
990
+     */
991
+    public function userDeletedFromGroup($uid, $gid) {
992
+    }
993
+
994
+    /**
995
+     * get database row of a give share
996
+     *
997
+     * @param $id
998
+     * @return array
999
+     * @throws ShareNotFound
1000
+     */
1001
+    protected function getRawShare($id) {
1002
+
1003
+        // Now fetch the inserted share and create a complete share object
1004
+        $qb = $this->dbConnection->getQueryBuilder();
1005
+        $qb->select('*')
1006
+            ->from('share')
1007
+            ->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
1008
+
1009
+        $cursor = $qb->execute();
1010
+        $data = $cursor->fetch();
1011
+        $cursor->closeCursor();
1012
+
1013
+        if ($data === false) {
1014
+            throw new ShareNotFound;
1015
+        }
1016
+
1017
+        return $data;
1018
+    }
1019
+
1020
+    public function getSharesInFolder($userId, Folder $node, $reshares) {
1021
+        $qb = $this->dbConnection->getQueryBuilder();
1022
+        $qb->select('*')
1023
+            ->from('share', 's')
1024
+            ->andWhere($qb->expr()->orX(
1025
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1026
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1027
+            ))
1028
+            ->andWhere(
1029
+                $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))
1030
+            );
1031
+
1032
+        /**
1033
+         * Reshares for this user are shares where they are the owner.
1034
+         */
1035
+        if ($reshares === false) {
1036
+            $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)));
1037
+        } else {
1038
+            $qb->andWhere(
1039
+                $qb->expr()->orX(
1040
+                    $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)),
1041
+                    $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))
1042
+                )
1043
+            );
1044
+        }
1045
+
1046
+        $qb->innerJoin('s', 'filecache' ,'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
1047
+        $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
1048
+
1049
+        $qb->orderBy('id');
1050
+
1051
+        $cursor = $qb->execute();
1052
+        $shares = [];
1053
+        while ($data = $cursor->fetch()) {
1054
+            $shares[$data['fileid']][] = $this->createShareObject($data);
1055
+        }
1056
+        $cursor->closeCursor();
1057
+
1058
+        return $shares;
1059
+    }
1060
+
1061
+    /**
1062
+     * @inheritdoc
1063
+     */
1064
+    public function getAccessList($nodes, $currentAccess) {
1065
+        $ids = [];
1066
+        foreach ($nodes as $node) {
1067
+            $ids[] = $node->getId();
1068
+        }
1069
+
1070
+        $qb = $this->dbConnection->getQueryBuilder();
1071
+        $qb->select('share_with')
1072
+            ->from('share')
1073
+            ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)))
1074
+            ->andWhere($qb->expr()->in('file_source', $qb->createNamedParameter($ids, IQueryBuilder::PARAM_INT_ARRAY)))
1075
+            ->andWhere($qb->expr()->orX(
1076
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
1077
+                $qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
1078
+            ))
1079
+            ->setMaxResults(1);
1080
+        $cursor = $qb->execute();
1081
+
1082
+        $mail = $cursor->fetch() !== false;
1083
+        $cursor->closeCursor();
1084
+
1085
+        return ['public' => $mail];
1086
+    }
1087 1087
 
1088 1088
 }
Please login to merge, or discard this patch.
apps/federation/lib/SyncJob.php 1 patch
Indentation   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -28,32 +28,32 @@
 block discarded – undo
28 28
 
29 29
 class SyncJob extends TimedJob {
30 30
 
31
-	/** @var SyncFederationAddressBooks */
32
-	protected $syncService;
31
+    /** @var SyncFederationAddressBooks */
32
+    protected $syncService;
33 33
 
34
-	/** @var ILogger */
35
-	protected $logger;
34
+    /** @var ILogger */
35
+    protected $logger;
36 36
 
37
-	/**
38
-	 * @param SyncFederationAddressBooks $syncService
39
-	 * @param ILogger $logger
40
-	 */
41
-	public function __construct(SyncFederationAddressBooks $syncService, ILogger $logger) {
42
-		// Run once a day
43
-		$this->setInterval(24 * 60 * 60);
44
-		$this->syncService = $syncService;
45
-		$this->logger = $logger;
46
-	}
37
+    /**
38
+     * @param SyncFederationAddressBooks $syncService
39
+     * @param ILogger $logger
40
+     */
41
+    public function __construct(SyncFederationAddressBooks $syncService, ILogger $logger) {
42
+        // Run once a day
43
+        $this->setInterval(24 * 60 * 60);
44
+        $this->syncService = $syncService;
45
+        $this->logger = $logger;
46
+    }
47 47
 
48
-	protected function run($argument) {
49
-		$this->syncService->syncThemAll(function($url, $ex) {
50
-			if ($ex instanceof \Exception) {
51
-				$this->logger->logException($ex, [
52
-					'message' => "Error while syncing $url.",
53
-					'level' => ILogger::ERROR,
54
-					'app' => 'fed-sync',
55
-				]);
56
-			}
57
-		});
58
-	}
48
+    protected function run($argument) {
49
+        $this->syncService->syncThemAll(function($url, $ex) {
50
+            if ($ex instanceof \Exception) {
51
+                $this->logger->logException($ex, [
52
+                    'message' => "Error while syncing $url.",
53
+                    'level' => ILogger::ERROR,
54
+                    'app' => 'fed-sync',
55
+                ]);
56
+            }
57
+        });
58
+    }
59 59
 }
Please login to merge, or discard this patch.
apps/federation/lib/Middleware/AddServerMiddleware.php 1 patch
Indentation   +43 added lines, -43 removed lines patch added patch discarded remove patch
@@ -38,54 +38,54 @@
 block discarded – undo
38 38
 
39 39
 class AddServerMiddleware extends Middleware {
40 40
 
41
-	/** @var  string */
42
-	protected $appName;
41
+    /** @var  string */
42
+    protected $appName;
43 43
 
44
-	/** @var  IL10N */
45
-	protected $l;
44
+    /** @var  IL10N */
45
+    protected $l;
46 46
 
47
-	/** @var  ILogger */
48
-	protected $logger;
47
+    /** @var  ILogger */
48
+    protected $logger;
49 49
 
50
-	/**
51
-	 * @param string $appName
52
-	 * @param IL10N $l
53
-	 * @param ILogger $logger
54
-	 */
55
-	public function __construct($appName, IL10N $l, ILogger $logger) {
56
-		$this->appName = $appName;
57
-		$this->l = $l;
58
-		$this->logger = $logger;
59
-	}
50
+    /**
51
+     * @param string $appName
52
+     * @param IL10N $l
53
+     * @param ILogger $logger
54
+     */
55
+    public function __construct($appName, IL10N $l, ILogger $logger) {
56
+        $this->appName = $appName;
57
+        $this->l = $l;
58
+        $this->logger = $logger;
59
+    }
60 60
 
61
-	/**
62
-	 * Log error message and return a response which can be displayed to the user
63
-	 *
64
-	 * @param Controller $controller
65
-	 * @param string $methodName
66
-	 * @param \Exception $exception
67
-	 * @return JSONResponse
68
-	 * @throws \Exception
69
-	 */
70
-	public function afterException($controller, $methodName, \Exception $exception) {
71
-		if (($controller instanceof SettingsController) === false) {
72
-			throw $exception;
73
-		}
74
-		$this->logger->logException($exception, [
75
-			'level' => ILogger::ERROR,
76
-			'app' => $this->appName,
77
-		]);
78
-		if ($exception instanceof HintException) {
79
-			$message = $exception->getHint();
80
-		} else {
81
-			$message = $exception->getMessage();
82
-		}
61
+    /**
62
+     * Log error message and return a response which can be displayed to the user
63
+     *
64
+     * @param Controller $controller
65
+     * @param string $methodName
66
+     * @param \Exception $exception
67
+     * @return JSONResponse
68
+     * @throws \Exception
69
+     */
70
+    public function afterException($controller, $methodName, \Exception $exception) {
71
+        if (($controller instanceof SettingsController) === false) {
72
+            throw $exception;
73
+        }
74
+        $this->logger->logException($exception, [
75
+            'level' => ILogger::ERROR,
76
+            'app' => $this->appName,
77
+        ]);
78
+        if ($exception instanceof HintException) {
79
+            $message = $exception->getHint();
80
+        } else {
81
+            $message = $exception->getMessage();
82
+        }
83 83
 
84
-		return new JSONResponse(
85
-			['message' => $message],
86
-			Http::STATUS_BAD_REQUEST
87
-		);
84
+        return new JSONResponse(
85
+            ['message' => $message],
86
+            Http::STATUS_BAD_REQUEST
87
+        );
88 88
 
89
-	}
89
+    }
90 90
 
91 91
 }
Please login to merge, or discard this patch.
apps/federation/lib/TrustedServers.php 1 patch
Indentation   +248 added lines, -248 removed lines patch added patch discarded remove patch
@@ -41,252 +41,252 @@
 block discarded – undo
41 41
 
42 42
 class TrustedServers {
43 43
 
44
-	/** after a user list was exchanged at least once successfully */
45
-	const STATUS_OK = 1;
46
-	/** waiting for shared secret or initial user list exchange */
47
-	const STATUS_PENDING = 2;
48
-	/** something went wrong, misconfigured server, software bug,... user interaction needed */
49
-	const STATUS_FAILURE = 3;
50
-	/** remote server revoked access */
51
-	const STATUS_ACCESS_REVOKED = 4;
52
-
53
-	/** @var  dbHandler */
54
-	private $dbHandler;
55
-
56
-	/** @var  IClientService */
57
-	private $httpClientService;
58
-
59
-	/** @var ILogger */
60
-	private $logger;
61
-
62
-	/** @var IJobList */
63
-	private $jobList;
64
-
65
-	/** @var ISecureRandom */
66
-	private $secureRandom;
67
-
68
-	/** @var IConfig */
69
-	private $config;
70
-
71
-	/** @var EventDispatcherInterface */
72
-	private $dispatcher;
73
-
74
-	/** @var ITimeFactory */
75
-	private $timeFactory;
76
-
77
-	/**
78
-	 * @param DbHandler $dbHandler
79
-	 * @param IClientService $httpClientService
80
-	 * @param ILogger $logger
81
-	 * @param IJobList $jobList
82
-	 * @param ISecureRandom $secureRandom
83
-	 * @param IConfig $config
84
-	 * @param EventDispatcherInterface $dispatcher
85
-	 * @param ITimeFactory $timeFactory
86
-	 */
87
-	public function __construct(
88
-		DbHandler $dbHandler,
89
-		IClientService $httpClientService,
90
-		ILogger $logger,
91
-		IJobList $jobList,
92
-		ISecureRandom $secureRandom,
93
-		IConfig $config,
94
-		EventDispatcherInterface $dispatcher,
95
-		ITimeFactory $timeFactory
96
-	) {
97
-		$this->dbHandler = $dbHandler;
98
-		$this->httpClientService = $httpClientService;
99
-		$this->logger = $logger;
100
-		$this->jobList = $jobList;
101
-		$this->secureRandom = $secureRandom;
102
-		$this->config = $config;
103
-		$this->dispatcher = $dispatcher;
104
-		$this->timeFactory = $timeFactory;
105
-	}
106
-
107
-	/**
108
-	 * add server to the list of trusted servers
109
-	 *
110
-	 * @param $url
111
-	 * @return int server id
112
-	 */
113
-	public function addServer($url) {
114
-		$url = $this->updateProtocol($url);
115
-		$result = $this->dbHandler->addServer($url);
116
-		if ($result) {
117
-			$token = $this->secureRandom->generate(16);
118
-			$this->dbHandler->addToken($url, $token);
119
-			$this->jobList->add(
120
-				RequestSharedSecret::class,
121
-				[
122
-					'url' => $url,
123
-					'token' => $token,
124
-					'created' => $this->timeFactory->getTime()
125
-				]
126
-			);
127
-		}
128
-
129
-		return $result;
130
-	}
131
-
132
-	/**
133
-	 * enable/disable to automatically add servers to the list of trusted servers
134
-	 * once a federated share was created and accepted successfully
135
-	 *
136
-	 * @param bool $status
137
-	 */
138
-	public function setAutoAddServers($status) {
139
-		$value = $status ? '1' : '0';
140
-		$this->config->setAppValue('federation', 'autoAddServers', $value);
141
-	}
142
-
143
-	/**
144
-	 * return if we automatically add servers to the list of trusted servers
145
-	 * once a federated share was created and accepted successfully
146
-	 *
147
-	 * @return bool
148
-	 */
149
-	public function getAutoAddServers() {
150
-		$value = $this->config->getAppValue('federation', 'autoAddServers', '0');
151
-		return $value === '1';
152
-	}
153
-
154
-	/**
155
-	 * get shared secret for the given server
156
-	 *
157
-	 * @param string $url
158
-	 * @return string
159
-	 */
160
-	public function getSharedSecret($url) {
161
-		return $this->dbHandler->getSharedSecret($url);
162
-	}
163
-
164
-	/**
165
-	 * add shared secret for the given server
166
-	 *
167
-	 * @param string $url
168
-	 * @param $sharedSecret
169
-	 */
170
-	public function addSharedSecret($url, $sharedSecret) {
171
-		$this->dbHandler->addSharedSecret($url, $sharedSecret);
172
-	}
173
-
174
-	/**
175
-	 * remove server from the list of trusted servers
176
-	 *
177
-	 * @param int $id
178
-	 */
179
-	public function removeServer($id) {
180
-		$server = $this->dbHandler->getServerById($id);
181
-		$this->dbHandler->removeServer($id);
182
-		$event = new GenericEvent($server['url_hash']);
183
-		$this->dispatcher->dispatch('OCP\Federation\TrustedServerEvent::remove', $event);
184
-	}
185
-
186
-	/**
187
-	 * get all trusted servers
188
-	 *
189
-	 * @return array
190
-	 */
191
-	public function getServers() {
192
-		return $this->dbHandler->getAllServer();
193
-	}
194
-
195
-	/**
196
-	 * check if given server is a trusted Nextcloud server
197
-	 *
198
-	 * @param string $url
199
-	 * @return bool
200
-	 */
201
-	public function isTrustedServer($url) {
202
-		return $this->dbHandler->serverExists($url);
203
-	}
204
-
205
-	/**
206
-	 * set server status
207
-	 *
208
-	 * @param string $url
209
-	 * @param int $status
210
-	 */
211
-	public function setServerStatus($url, $status) {
212
-		$this->dbHandler->setServerStatus($url, $status);
213
-	}
214
-
215
-	/**
216
-	 * @param string $url
217
-	 * @return int
218
-	 */
219
-	public function getServerStatus($url) {
220
-		return $this->dbHandler->getServerStatus($url);
221
-	}
222
-
223
-	/**
224
-	 * check if URL point to a ownCloud/Nextcloud server
225
-	 *
226
-	 * @param string $url
227
-	 * @return bool
228
-	 */
229
-	public function isOwnCloudServer($url) {
230
-		$isValidOwnCloud = false;
231
-		$client = $this->httpClientService->newClient();
232
-		try {
233
-			$result = $client->get(
234
-				$url . '/status.php',
235
-				[
236
-					'timeout' => 3,
237
-					'connect_timeout' => 3,
238
-				]
239
-			);
240
-			if ($result->getStatusCode() === Http::STATUS_OK) {
241
-				$isValidOwnCloud = $this->checkOwnCloudVersion($result->getBody());
242
-
243
-			}
244
-		} catch (\Exception $e) {
245
-			\OC::$server->getLogger()->logException($e, [
246
-				'message' => 'No Nextcloud server.',
247
-				'level' => ILogger::DEBUG,
248
-				'app' => 'federation',
249
-			]);
250
-			return false;
251
-		}
252
-
253
-		return $isValidOwnCloud;
254
-	}
255
-
256
-	/**
257
-	 * check if ownCloud version is >= 9.0
258
-	 *
259
-	 * @param $status
260
-	 * @return bool
261
-	 * @throws HintException
262
-	 */
263
-	protected function checkOwnCloudVersion($status) {
264
-		$decoded = json_decode($status, true);
265
-		if (!empty($decoded) && isset($decoded['version'])) {
266
-			if (!version_compare($decoded['version'], '9.0.0', '>=')) {
267
-				throw new HintException('Remote server version is too low. 9.0 is required.');
268
-			}
269
-			return true;
270
-		}
271
-		return false;
272
-	}
273
-
274
-	/**
275
-	 * check if the URL contain a protocol, if not add https
276
-	 *
277
-	 * @param string $url
278
-	 * @return string
279
-	 */
280
-	protected function updateProtocol($url) {
281
-		if (
282
-			strpos($url, 'https://') === 0
283
-			|| strpos($url, 'http://') === 0
284
-		) {
285
-
286
-			return $url;
287
-
288
-		}
289
-
290
-		return 'https://' . $url;
291
-	}
44
+    /** after a user list was exchanged at least once successfully */
45
+    const STATUS_OK = 1;
46
+    /** waiting for shared secret or initial user list exchange */
47
+    const STATUS_PENDING = 2;
48
+    /** something went wrong, misconfigured server, software bug,... user interaction needed */
49
+    const STATUS_FAILURE = 3;
50
+    /** remote server revoked access */
51
+    const STATUS_ACCESS_REVOKED = 4;
52
+
53
+    /** @var  dbHandler */
54
+    private $dbHandler;
55
+
56
+    /** @var  IClientService */
57
+    private $httpClientService;
58
+
59
+    /** @var ILogger */
60
+    private $logger;
61
+
62
+    /** @var IJobList */
63
+    private $jobList;
64
+
65
+    /** @var ISecureRandom */
66
+    private $secureRandom;
67
+
68
+    /** @var IConfig */
69
+    private $config;
70
+
71
+    /** @var EventDispatcherInterface */
72
+    private $dispatcher;
73
+
74
+    /** @var ITimeFactory */
75
+    private $timeFactory;
76
+
77
+    /**
78
+     * @param DbHandler $dbHandler
79
+     * @param IClientService $httpClientService
80
+     * @param ILogger $logger
81
+     * @param IJobList $jobList
82
+     * @param ISecureRandom $secureRandom
83
+     * @param IConfig $config
84
+     * @param EventDispatcherInterface $dispatcher
85
+     * @param ITimeFactory $timeFactory
86
+     */
87
+    public function __construct(
88
+        DbHandler $dbHandler,
89
+        IClientService $httpClientService,
90
+        ILogger $logger,
91
+        IJobList $jobList,
92
+        ISecureRandom $secureRandom,
93
+        IConfig $config,
94
+        EventDispatcherInterface $dispatcher,
95
+        ITimeFactory $timeFactory
96
+    ) {
97
+        $this->dbHandler = $dbHandler;
98
+        $this->httpClientService = $httpClientService;
99
+        $this->logger = $logger;
100
+        $this->jobList = $jobList;
101
+        $this->secureRandom = $secureRandom;
102
+        $this->config = $config;
103
+        $this->dispatcher = $dispatcher;
104
+        $this->timeFactory = $timeFactory;
105
+    }
106
+
107
+    /**
108
+     * add server to the list of trusted servers
109
+     *
110
+     * @param $url
111
+     * @return int server id
112
+     */
113
+    public function addServer($url) {
114
+        $url = $this->updateProtocol($url);
115
+        $result = $this->dbHandler->addServer($url);
116
+        if ($result) {
117
+            $token = $this->secureRandom->generate(16);
118
+            $this->dbHandler->addToken($url, $token);
119
+            $this->jobList->add(
120
+                RequestSharedSecret::class,
121
+                [
122
+                    'url' => $url,
123
+                    'token' => $token,
124
+                    'created' => $this->timeFactory->getTime()
125
+                ]
126
+            );
127
+        }
128
+
129
+        return $result;
130
+    }
131
+
132
+    /**
133
+     * enable/disable to automatically add servers to the list of trusted servers
134
+     * once a federated share was created and accepted successfully
135
+     *
136
+     * @param bool $status
137
+     */
138
+    public function setAutoAddServers($status) {
139
+        $value = $status ? '1' : '0';
140
+        $this->config->setAppValue('federation', 'autoAddServers', $value);
141
+    }
142
+
143
+    /**
144
+     * return if we automatically add servers to the list of trusted servers
145
+     * once a federated share was created and accepted successfully
146
+     *
147
+     * @return bool
148
+     */
149
+    public function getAutoAddServers() {
150
+        $value = $this->config->getAppValue('federation', 'autoAddServers', '0');
151
+        return $value === '1';
152
+    }
153
+
154
+    /**
155
+     * get shared secret for the given server
156
+     *
157
+     * @param string $url
158
+     * @return string
159
+     */
160
+    public function getSharedSecret($url) {
161
+        return $this->dbHandler->getSharedSecret($url);
162
+    }
163
+
164
+    /**
165
+     * add shared secret for the given server
166
+     *
167
+     * @param string $url
168
+     * @param $sharedSecret
169
+     */
170
+    public function addSharedSecret($url, $sharedSecret) {
171
+        $this->dbHandler->addSharedSecret($url, $sharedSecret);
172
+    }
173
+
174
+    /**
175
+     * remove server from the list of trusted servers
176
+     *
177
+     * @param int $id
178
+     */
179
+    public function removeServer($id) {
180
+        $server = $this->dbHandler->getServerById($id);
181
+        $this->dbHandler->removeServer($id);
182
+        $event = new GenericEvent($server['url_hash']);
183
+        $this->dispatcher->dispatch('OCP\Federation\TrustedServerEvent::remove', $event);
184
+    }
185
+
186
+    /**
187
+     * get all trusted servers
188
+     *
189
+     * @return array
190
+     */
191
+    public function getServers() {
192
+        return $this->dbHandler->getAllServer();
193
+    }
194
+
195
+    /**
196
+     * check if given server is a trusted Nextcloud server
197
+     *
198
+     * @param string $url
199
+     * @return bool
200
+     */
201
+    public function isTrustedServer($url) {
202
+        return $this->dbHandler->serverExists($url);
203
+    }
204
+
205
+    /**
206
+     * set server status
207
+     *
208
+     * @param string $url
209
+     * @param int $status
210
+     */
211
+    public function setServerStatus($url, $status) {
212
+        $this->dbHandler->setServerStatus($url, $status);
213
+    }
214
+
215
+    /**
216
+     * @param string $url
217
+     * @return int
218
+     */
219
+    public function getServerStatus($url) {
220
+        return $this->dbHandler->getServerStatus($url);
221
+    }
222
+
223
+    /**
224
+     * check if URL point to a ownCloud/Nextcloud server
225
+     *
226
+     * @param string $url
227
+     * @return bool
228
+     */
229
+    public function isOwnCloudServer($url) {
230
+        $isValidOwnCloud = false;
231
+        $client = $this->httpClientService->newClient();
232
+        try {
233
+            $result = $client->get(
234
+                $url . '/status.php',
235
+                [
236
+                    'timeout' => 3,
237
+                    'connect_timeout' => 3,
238
+                ]
239
+            );
240
+            if ($result->getStatusCode() === Http::STATUS_OK) {
241
+                $isValidOwnCloud = $this->checkOwnCloudVersion($result->getBody());
242
+
243
+            }
244
+        } catch (\Exception $e) {
245
+            \OC::$server->getLogger()->logException($e, [
246
+                'message' => 'No Nextcloud server.',
247
+                'level' => ILogger::DEBUG,
248
+                'app' => 'federation',
249
+            ]);
250
+            return false;
251
+        }
252
+
253
+        return $isValidOwnCloud;
254
+    }
255
+
256
+    /**
257
+     * check if ownCloud version is >= 9.0
258
+     *
259
+     * @param $status
260
+     * @return bool
261
+     * @throws HintException
262
+     */
263
+    protected function checkOwnCloudVersion($status) {
264
+        $decoded = json_decode($status, true);
265
+        if (!empty($decoded) && isset($decoded['version'])) {
266
+            if (!version_compare($decoded['version'], '9.0.0', '>=')) {
267
+                throw new HintException('Remote server version is too low. 9.0 is required.');
268
+            }
269
+            return true;
270
+        }
271
+        return false;
272
+    }
273
+
274
+    /**
275
+     * check if the URL contain a protocol, if not add https
276
+     *
277
+     * @param string $url
278
+     * @return string
279
+     */
280
+    protected function updateProtocol($url) {
281
+        if (
282
+            strpos($url, 'https://') === 0
283
+            || strpos($url, 'http://') === 0
284
+        ) {
285
+
286
+            return $url;
287
+
288
+        }
289
+
290
+        return 'https://' . $url;
291
+    }
292 292
 }
Please login to merge, or discard this patch.