Completed
Push — stable8.1 ( 36a2d1...c01ad6 )
by Morris
104:35
created

Shared_Updater::postShareHook()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 7.0487
Metric Value
dl 0
loc 16
ccs 9
cts 13
cp 0.6923
rs 8.8571
cc 6
eloc 9
nc 5
nop 1
crap 7.0487
1
<?php
2
/**
3
 * @author Björn Schießle <[email protected]>
4
 * @author Joas Schilling <[email protected]>
5
 * @author Michael Gapczynski <[email protected]>
6
 * @author Morris Jobke <[email protected]>
7
 * @author Vincent Petry <[email protected]>
8
 *
9
 * @copyright Copyright (c) 2015, ownCloud, Inc.
10
 * @license AGPL-3.0
11
 *
12
 * This code is free software: you can redistribute it and/or modify
13
 * it under the terms of the GNU Affero General Public License, version 3,
14
 * as published by the Free Software Foundation.
15
 *
16
 * This program is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
 * GNU Affero General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU Affero General Public License, version 3,
22
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
23
 *
24
 */
25
26
namespace OC\Files\Cache;
27
28
class Shared_Updater {
29
30
	/**
31
	 * Walk up the users file tree and update the etags.
32
	 *
33
	 * @param string $user user id
34
	 * @param string $path share mount point path, relative to the user's "files" folder
35
	 */
36 27
	static private function correctUsersFolder($user, $path) {
37
		// $path points to the mount point which is a virtual folder, so we start with
38
		// the parent
39 27
		$path = '/' . ltrim($path, '/');
40 27
		$path = '/files' . dirname($path);
41 27
		\OC\Files\Filesystem::initMountPoints($user);
42 27
		$view = new \OC\Files\View('/' . $user);
43 27
		if ($view->file_exists($path)) {
44 25
			while ($path !== dirname($path)) {
45 25
				$etag = $view->getETag($path);
46 25
				$view->putFileInfo($path, array('etag' => $etag));
47 25
				$path = dirname($path);
48 25
			}
49 25
		} else {
50 2
			\OCP\Util::writeLog('files_sharing', 'can not update etags on ' . $path . ' for user ' . $user . '. Path does not exists', \OCP\Util::DEBUG);
51
		}
52 27
	}
53
54
	/**
55
	 * @param array $params
56
	 */
57 3
	static public function renameHook($params) {
58 3
		self::renameChildren($params['oldpath'], $params['newpath']);
59 3
		self::moveShareToShare($params['newpath']);
60 3
	}
61
62
	/**
63
	 * Fix for https://github.com/owncloud/core/issues/20769
64
	 *
65
	 * The owner is allowed to move their files (if they are shared) into a receiving folder
66
	 * In this case we need to update the parent of the moved share. Since they are
67
	 * effectively handing over ownership of the file the rest of the code needs to know
68
	 * they need to build up the reshare tree.
69
	 *
70
	 * @param string $path
71
	 */
72 3
	static private function moveShareToShare($path) {
73 3
		$userFolder = \OC::$server->getUserFolder();
74
75
		// If the user folder can't be constructed (e.g. link share) just return.
76 3
		if ($userFolder === null) {
77
			return;
78
		}
79
80 3
		$src = $userFolder->get($path);
81
82 3
		$type = $src instanceof \OCP\Files\File ? 'file' : 'folder';
83 3
		$shares = \OCP\Share::getItemShared($type, $src->getId());
84
85
		// If the path we move is not a share we don't care
86 3
		if (empty($shares)) {
87 3
			return;
88
		}
89
90
		// Check if the destination is inside a share
91
		$mountManager = \OC\Files\Filesystem::getMountManager();
92
		$dstMount = $mountManager->find($src->getPath());
93
		if (!($dstMount instanceof \OCA\Files_Sharing\SharedMount)) {
94
			return;
95
		}
96
97
		$parenShare = $dstMount->getShare();
98
99
		foreach ($shares as $share) {
100
			$query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `parent` = ? WHERE `id` = ?');
101
			$query->execute([$parenShare['id'], $share['id']]);
102
		}
103
	}
104
105
	/**
106
	 * @param array $params
107
	 */
108 9
	static public function deleteHook($params) {
109 9
		$path = $params['path'];
0 ignored issues
show
Unused Code introduced by
$path is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
110 9
	}
111
112
	/**
113
	 * update etags if a file was shared
114
	 * @param array $params
115
	 */
116 27
	static public function postShareHook($params) {
117
118 27
		if ($params['itemType'] === 'folder' || $params['itemType'] === 'file') {
119
120 27
			$shareWith = $params['shareWith'];
121 27
			$shareType = $params['shareType'];
122
123 27
			if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
124 27
				self::correctUsersFolder($shareWith, $params['fileTarget']);
125 27
			} elseif ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
126
				foreach (\OC_Group::usersInGroup($shareWith) as $user) {
127
					self::correctUsersFolder($user, $params['fileTarget']);
128
				}
129
			}
130 27
		}
131 27
	}
132
133
	/**
134
	 * update etags if a file was unshared
135
	 *
136
	 * @param array $params
137
	 */
138 4
	static public function postUnshareHook($params) {
139
140
		// only update etags for file/folders shared to local users/groups
141 4
		if (($params['itemType'] === 'file' || $params['itemType'] === 'folder') &&
142 4
				$params['shareType'] !== \OCP\Share::SHARE_TYPE_LINK &&
143 4
				$params['shareType'] !== \OCP\Share::SHARE_TYPE_REMOTE) {
144
145 4
			$deletedShares = isset($params['deletedShares']) ? $params['deletedShares'] : array();
146
147 4 View Code Duplication
			foreach ($deletedShares as $share) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
148 4
				if ($share['shareType'] === \OCP\Share::SHARE_TYPE_GROUP) {
149
					foreach (\OC_Group::usersInGroup($share['shareWith']) as $user) {
150
						self::correctUsersFolder($user, $share['fileTarget']);
151
					}
152
				} else {
153 4
					self::correctUsersFolder($share['shareWith'], $share['fileTarget']);
154
				}
155 4
			}
156 4
		}
157 4
	}
158
159
	/**
160
	 * update etags if file was unshared from self
161
	 * @param array $params
162
	 */
163 2
	static public function postUnshareFromSelfHook($params) {
164 2
		if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
165 2 View Code Duplication
			foreach ($params['unsharedItems'] as $item) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
166 2
				if ($item['shareType'] === \OCP\Share::SHARE_TYPE_GROUP) {
167
					foreach (\OC_Group::usersInGroup($item['shareWith']) as $user) {
168
						self::correctUsersFolder($user, $item['fileTarget']);
169
					}
170
				} else {
171 2
					self::correctUsersFolder($item['shareWith'], $item['fileTarget']);
172
				}
173 2
			}
174 2
		}
175 2
	}
176
177
	/**
178
	 * clean up oc_share table from files which are no longer exists
179
	 *
180
	 * This fixes issues from updates from files_sharing < 0.3.5.6 (ownCloud 4.5)
181
	 * It will just be called during the update of the app
182
	 */
183
	static public function fixBrokenSharesOnAppUpdate() {
184
		// delete all shares where the original file no longer exists
185
		$findAndRemoveShares = \OC_DB::prepare('DELETE FROM `*PREFIX*share` ' .
186
			'WHERE `item_type` IN (\'file\', \'folder\') ' .
187
			'AND `file_source` NOT IN (SELECT `fileid` FROM `*PREFIX*filecache`)'
188
		);
189
		$findAndRemoveShares->execute(array());
190
	}
191
192
	/**
193
	 * rename mount point from the children if the parent was renamed
194
	 *
195
	 * @param string $oldPath old path relative to data/user/files
196
	 * @param string $newPath new path relative to data/user/files
197
	 */
198 3
	static private function renameChildren($oldPath, $newPath) {
199
200 3
		$absNewPath =  \OC\Files\Filesystem::normalizePath('/' . \OCP\User::getUser() . '/files/' . $newPath);
201 3
		$absOldPath =  \OC\Files\Filesystem::normalizePath('/' . \OCP\User::getUser() . '/files/' . $oldPath);
202
203 3
		$mountManager = \OC\Files\Filesystem::getMountManager();
204 3
		$mountedShares = $mountManager->findIn('/' . \OCP\User::getUser() . '/files/' . $oldPath);
205 3
		foreach ($mountedShares as $mount) {
206 1
			if ($mount->getStorage()->instanceOfStorage('OCA\Files_Sharing\ISharedStorage')) {
207 1
				$mountPoint = $mount->getMountPoint();
208 1
				$target = str_replace($absOldPath, $absNewPath, $mountPoint);
209 1
				$mount->moveMount($target);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class OC\Files\Mount\MountPoint as the method moveMount() does only exist in the following sub-classes of OC\Files\Mount\MountPoint: DummyTestClassSharedMount, OCA\Files_External\PersonalMount, OCA\Files_Sharing\External\Mount, OCA\Files_Sharing\SharedMount, Test\TestMoveableMountPoint. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
210 1
			}
211 3
		}
212 3
	}
213
214
}
215