1212 * @author Roeland Jago Douma <[email protected] > 1313 * @author Sascha Wiswedel <[email protected] > 1414 * @author Tobia De Koninck <[email protected] > 15+ * @author Ferdinand Thiessen <[email protected] > 1516 *
1617 * @license GNU AGPL version 3 or any later version
1718 *
3334namespace OCA \Files \Service ;
3435
3536use Closure ;
37+ use OC \Encryption \Manager as EncryptionManager ;
3638use OC \Files \Filesystem ;
3739use OC \Files \View ;
3840use OCA \Files \Exception \TransferOwnershipException ;
4143use OCP \Files \FileInfo ;
4244use OCP \Files \IHomeStorage ;
4345use OCP \Files \InvalidPathException ;
46+ use OCP \Files \IRootFolder ;
4447use OCP \Files \Mount \IMountManager ;
4548use OCP \IUser ;
4649use OCP \IUserManager ;
5861
5962class OwnershipTransferService {
6063
61- /** @var IEncryptionManager */
62- private $ encryptionManager ;
64+ private IEncryptionManager |EncryptionManager $ encryptionManager ;
6365
64- /** @var IShareManager */
65- private $ shareManager ;
66-
67- /** @var IMountManager */
68- private $ mountManager ;
69-
70- /** @var IUserMountCache */
71- private $ userMountCache ;
72-
73- /** @var IUserManager */
74- private $ userManager ;
75-
76- public function __construct (IEncryptionManager $ manager ,
77- IShareManager $ shareManager ,
78- IMountManager $ mountManager ,
79- IUserMountCache $ userMountCache ,
80- IUserManager $ userManager ) {
81- $ this ->encryptionManager = $ manager ;
82- $ this ->shareManager = $ shareManager ;
83- $ this ->mountManager = $ mountManager ;
84- $ this ->userMountCache = $ userMountCache ;
85- $ this ->userManager = $ userManager ;
66+ public function __construct (
67+ IEncryptionManager $ encryptionManager ,
68+ private IShareManager $ shareManager ,
69+ private IMountManager $ mountManager ,
70+ private IUserMountCache $ userMountCache ,
71+ private IUserManager $ userManager ,
72+ ) {
73+ $ this ->encryptionManager = $ encryptionManager ;
8674 }
8775
8876 /**
@@ -95,13 +83,15 @@ public function __construct(IEncryptionManager $manager,
9583 * @throws TransferOwnershipException
9684 * @throws \OC\User\NoUserException
9785 */
98- public function transfer (IUser $ sourceUser ,
86+ public function transfer (
87+ IUser $ sourceUser ,
9988 IUser $ destinationUser ,
10089 string $ path ,
10190 ?OutputInterface $ output = null ,
10291 bool $ move = false ,
10392 bool $ firstLogin = false ,
104- bool $ transferIncomingShares = false ): void {
93+ bool $ transferIncomingShares = false ,
94+ ): void {
10595 $ output = $ output ?? new NullOutput ();
10696 $ sourceUid = $ sourceUser ->getUID ();
10797 $ destinationUid = $ destinationUser ->getUID ();
@@ -183,10 +173,12 @@ public function transfer(IUser $sourceUser,
183173 $ output
184174 );
185175
176+ $ destinationPath = $ finalTarget . '/ ' . $ path ;
186177 // restore the shares
187178 $ this ->restoreShares (
188179 $ sourceUid ,
189180 $ destinationUid ,
181+ $ destinationPath ,
190182 $ shares ,
191183 $ output
192184 );
@@ -280,16 +272,35 @@ function (FileInfo $fileInfo) use ($progress) {
280272 }
281273 }
282274
283- private function collectUsersShares (string $ sourceUid ,
275+ /**
276+ * @return array<array{share: IShare, suffix: string}>
277+ */
278+ private function collectUsersShares (
279+ string $ sourceUid ,
284280 OutputInterface $ output ,
285281 View $ view ,
286- string $ path ): array {
282+ string $ path ,
283+ ): array {
287284 $ output ->writeln ("Collecting all share information for files and folders of $ sourceUid ... " );
288285
289286 $ shares = [];
290287 $ progress = new ProgressBar ($ output );
291288
292- foreach ([IShare::TYPE_GROUP , IShare::TYPE_USER , IShare::TYPE_LINK , IShare::TYPE_REMOTE , IShare::TYPE_ROOM , IShare::TYPE_EMAIL , IShare::TYPE_CIRCLE , IShare::TYPE_DECK , IShare::TYPE_SCIENCEMESH ] as $ shareType ) {
289+ $ normalizedPath = Filesystem::normalizePath ($ path );
290+
291+ $ supportedShareTypes = [
292+ IShare::TYPE_GROUP ,
293+ IShare::TYPE_USER ,
294+ IShare::TYPE_LINK ,
295+ IShare::TYPE_REMOTE ,
296+ IShare::TYPE_ROOM ,
297+ IShare::TYPE_EMAIL ,
298+ IShare::TYPE_CIRCLE ,
299+ IShare::TYPE_DECK ,
300+ IShare::TYPE_SCIENCEMESH ,
301+ ];
302+
303+ foreach ($ supportedShareTypes as $ shareType ) {
293304 $ offset = 0 ;
294305 while (true ) {
295306 $ sharePage = $ this ->shareManager ->getSharesBy ($ sourceUid , $ shareType , null , true , 50 , $ offset );
@@ -298,17 +309,17 @@ private function collectUsersShares(string $sourceUid,
298309 break ;
299310 }
300311 if ($ path !== "$ sourceUid/files " ) {
301- $ sharePage = array_filter ($ sharePage , function (IShare $ share ) use ($ view , $ path ) {
312+ $ sharePage = array_filter ($ sharePage , function (IShare $ share ) use ($ view , $ normalizedPath ) {
302313 try {
303314 $ relativePath = $ view ->getPath ($ share ->getNodeId ());
304- $ singleFileTranfer = $ view ->is_file ($ path );
315+ $ singleFileTranfer = $ view ->is_file ($ normalizedPath );
305316 if ($ singleFileTranfer ) {
306- return Filesystem::normalizePath ($ relativePath ) === Filesystem:: normalizePath ( $ path ) ;
317+ return Filesystem::normalizePath ($ relativePath ) === $ normalizedPath ;
307318 }
308319
309320 return mb_strpos (
310321 Filesystem::normalizePath ($ relativePath . '/ ' , false ),
311- Filesystem:: normalizePath ( $ path . '/ ' , false ) ) === 0 ;
322+ $ normalizedPath . '/ ' ) === 0 ;
312323 } catch (\Exception $ e ) {
313324 return false ;
314325 }
@@ -321,7 +332,11 @@ private function collectUsersShares(string $sourceUid,
321332
322333 $ progress ->finish ();
323334 $ output ->writeln ('' );
324- return $ shares ;
335+
336+ return array_map (fn (IShare $ share ) => [
337+ 'share ' => $ share ,
338+ 'suffix ' => substr (Filesystem::normalizePath ($ view ->getPath ($ share ->getNodeId ())), strlen ($ normalizedPath )),
339+ ], $ shares );
325340 }
326341
327342 private function collectIncomingShares (string $ sourceUid ,
@@ -384,14 +399,22 @@ protected function transferFiles(string $sourceUid,
384399 }
385400 }
386401
387- private function restoreShares (string $ sourceUid ,
402+ /**
403+ * @param string $targetLocation New location of the transfered node
404+ * @param array<array{share: IShare, suffix: string}> $shares previously collected share information
405+ */
406+ private function restoreShares (
407+ string $ sourceUid ,
388408 string $ destinationUid ,
409+ string $ targetLocation ,
389410 array $ shares ,
390- OutputInterface $ output ) {
411+ OutputInterface $ output ,
412+ ):void {
391413 $ output ->writeln ("Restoring shares ... " );
392414 $ progress = new ProgressBar ($ output , count ($ shares ));
415+ $ rootFolder = \OCP \Server::get (IRootFolder::class);
393416
394- foreach ($ shares as $ share ) {
417+ foreach ($ shares as [ ' share ' => $ share, ' suffix ' => $ suffix ] ) {
395418 try {
396419 if ($ share ->getShareType () === IShare::TYPE_USER &&
397420 $ share ->getSharedWith () === $ destinationUid ) {
@@ -419,7 +442,19 @@ private function restoreShares(string $sourceUid,
419442 // trigger refetching of the node so that the new owner and mountpoint are taken into account
420443 // otherwise the checks on the share update will fail due to the original node not being available in the new user scope
421444 $ this ->userMountCache ->clear ();
422- $ share ->setNodeId ($ share ->getNode ()->getId ());
445+
446+ try {
447+ // Try to get the "old" id.
448+ // Normally the ID is preserved,
449+ // but for transferes between different storages the ID might change
450+ $ newNodeId = $ share ->getNode ()->getId ();
451+ } catch (\OCP \Files \NotFoundException ) {
452+ // ID has changed due to transfer between different storages
453+ // Try to get the new ID from the target path and suffix of the share
454+ $ node = $ rootFolder ->get (Filesystem::normalizePath ($ targetLocation . '/ ' . $ suffix ));
455+ $ newNodeId = $ node ->getId ();
456+ }
457+ $ share ->setNodeId ($ newNodeId );
423458
424459 $ this ->shareManager ->updateShare ($ share );
425460 }
0 commit comments