Completed
Push — stable9 ( 485cb1...e094cf )
by Lukas
26:41 queued 26:23
created

Shared_Updater::postShareHook()   B

Complexity

Conditions 6
Paths 5

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 9
nc 5
nop 1
dl 0
loc 16
rs 8.8571
c 0
b 0
f 0
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Björn Schießle <[email protected]>
6
 * @author Joas Schilling <[email protected]>
7
 * @author Michael Gapczynski <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Robin Appelman <[email protected]>
10
 * @author Roeland Jago Douma <[email protected]>
11
 * @author Vincent Petry <[email protected]>
12
 *
13
 * @license AGPL-3.0
14
 *
15
 * This code is free software: you can redistribute it and/or modify
16
 * it under the terms of the GNU Affero General Public License, version 3,
17
 * as published by the Free Software Foundation.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22
 * GNU Affero General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU Affero General Public License, version 3,
25
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
26
 *
27
 */
28
29
namespace OC\Files\Cache;
30
31
class Shared_Updater {
32
33
	/**
34
	 * Walk up the users file tree and update the etags.
35
	 *
36
	 * @param string $user user id
37
	 * @param string $path share mount point path, relative to the user's "files" folder
38
	 */
39
	static private function correctUsersFolder($user, $path) {
40
		// $path points to the mount point which is a virtual folder, so we start with
41
		// the parent
42
		$path = '/' . ltrim($path, '/');
43
		$path = '/files' . dirname($path);
44
		\OC\Files\Filesystem::initMountPoints($user);
45
		$view = new \OC\Files\View('/' . $user);
46
		if ($view->file_exists($path)) {
47
			while ($path !== dirname($path)) {
48
				$etag = $view->getETag($path);
49
				$view->putFileInfo($path, array('etag' => $etag));
50
				$path = dirname($path);
51
			}
52
		} else {
53
			\OCP\Util::writeLog('files_sharing', 'can not update etags on ' . $path . ' for user ' . $user . '. Path does not exists', \OCP\Util::DEBUG);
54
		}
55
	}
56
57
	/**
58
	 * @param array $params
59
	 */
60
	static public function renameHook($params) {
61
		self::renameChildren($params['oldpath'], $params['newpath']);
62
		self::moveShareToShare($params['newpath']);
63
	}
64
65
	/**
66
	 * Fix for https://github.com/owncloud/core/issues/20769
67
	 *
68
	 * The owner is allowed to move their files (if they are shared) into a receiving folder
69
	 * In this case we need to update the parent of the moved share. Since they are
70
	 * effectively handing over ownership of the file the rest of the code needs to know
71
	 * they need to build up the reshare tree.
72
	 *
73
	 * @param string $path
74
	 */
75
	static private function moveShareToShare($path) {
76
		$userFolder = \OC::$server->getUserFolder();
77
78
		// If the user folder can't be constructed (e.g. link share) just return.
79
		if ($userFolder === null) {
80
			return;
81
		}
82
83
		$src = $userFolder->get($path);
84
85
		$type = $src instanceof \OCP\Files\File ? 'file' : 'folder';
86
		$shares = \OCP\Share::getItemShared($type, $src->getId());
87
88
		// If the path we move is not a share we don't care
89
		if (empty($shares)) {
90
			return;
91
		}
92
93
		// Check if the destination is inside a share
94
		$mountManager = \OC::$server->getMountManager();
95
		$dstMount = $mountManager->find($src->getPath());
96
		if (!($dstMount instanceof \OCA\Files_Sharing\SharedMount)) {
97
			return;
98
		}
99
100
		$parenShare = $dstMount->getShare();
101
102
		foreach ($shares as $share) {
103
			$qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
104
			$qb->update('share')
105
					->set('parent', $qb->createNamedParameter($parenShare['id']))
106
					->where($qb->expr()->eq('id', $qb->createNamedParameter($share['id'])))
107
					->execute();
108
		}
109
	}
110
111
	/**
112
	 * @param array $params
113
	 */
114
	static public function deleteHook($params) {
115
		$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...
116
	}
117
118
	/**
119
	 * update etags if a file was shared
120
	 * @param array $params
121
	 */
122
	static public function postShareHook($params) {
123
124
		if ($params['itemType'] === 'folder' || $params['itemType'] === 'file') {
125
126
			$shareWith = $params['shareWith'];
127
			$shareType = $params['shareType'];
128
129
			if ($shareType === \OCP\Share::SHARE_TYPE_USER) {
130
				self::correctUsersFolder($shareWith, $params['fileTarget']);
131
			} elseif ($shareType === \OCP\Share::SHARE_TYPE_GROUP) {
132
				foreach (\OC_Group::usersInGroup($shareWith) as $user) {
133
					self::correctUsersFolder($user, $params['fileTarget']);
134
				}
135
			}
136
		}
137
	}
138
139
	/**
140
	 * update etags if a file was unshared
141
	 *
142
	 * @param array $params
143
	 */
144
	static public function postUnshareHook($params) {
145
146
		// only update etags for file/folders shared to local users/groups
147
		if (($params['itemType'] === 'file' || $params['itemType'] === 'folder') &&
148
				$params['shareType'] !== \OCP\Share::SHARE_TYPE_LINK &&
149
				$params['shareType'] !== \OCP\Share::SHARE_TYPE_REMOTE) {
150
151
			$deletedShares = isset($params['deletedShares']) ? $params['deletedShares'] : array();
152
153 View Code Duplication
			foreach ($deletedShares as $share) {
154
				if ($share['shareType'] === \OCP\Share::SHARE_TYPE_GROUP) {
155
					foreach (\OC_Group::usersInGroup($share['shareWith']) as $user) {
156
						self::correctUsersFolder($user, $share['fileTarget']);
157
					}
158
				} else {
159
					self::correctUsersFolder($share['shareWith'], $share['fileTarget']);
160
				}
161
			}
162
		}
163
	}
164
165
	/**
166
	 * update etags if file was unshared from self
167
	 * @param array $params
168
	 */
169
	static public function postUnshareFromSelfHook($params) {
170
		if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') {
171 View Code Duplication
			foreach ($params['unsharedItems'] as $item) {
172
				if ($item['shareType'] === \OCP\Share::SHARE_TYPE_GROUP) {
173
					foreach (\OC_Group::usersInGroup($item['shareWith']) as $user) {
174
						self::correctUsersFolder($user, $item['fileTarget']);
175
					}
176
				} else {
177
					self::correctUsersFolder($item['shareWith'], $item['fileTarget']);
178
				}
179
			}
180
		}
181
	}
182
183
	/**
184
	 * clean up oc_share table from files which are no longer exists
185
	 *
186
	 * This fixes issues from updates from files_sharing < 0.3.5.6 (ownCloud 4.5)
187
	 * It will just be called during the update of the app
188
	 */
189
	static public function fixBrokenSharesOnAppUpdate() {
190
		// delete all shares where the original file no longer exists
191
		$findAndRemoveShares = \OCP\DB::prepare('DELETE FROM `*PREFIX*share` ' .
0 ignored issues
show
Deprecated Code introduced by
The method OCP\DB::prepare() has been deprecated with message: 8.1.0 use prepare() of \OCP\IDBConnection - \OC::$server->getDatabaseConnection()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
192
			'WHERE `item_type` IN (\'file\', \'folder\') ' .
193
			'AND `file_source` NOT IN (SELECT `fileid` FROM `*PREFIX*filecache`)'
194
		);
195
		$findAndRemoveShares->execute(array());
196
	}
197
198
	/**
199
	 * rename mount point from the children if the parent was renamed
200
	 *
201
	 * @param string $oldPath old path relative to data/user/files
202
	 * @param string $newPath new path relative to data/user/files
203
	 */
204
	static private function renameChildren($oldPath, $newPath) {
205
206
		$absNewPath =  \OC\Files\Filesystem::normalizePath('/' . \OCP\User::getUser() . '/files/' . $newPath);
0 ignored issues
show
Deprecated Code introduced by
The method OCP\User::getUser() has been deprecated with message: 8.0.0 Use \OC::$server->getUserSession()->getUser()->getUID()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
207
		$absOldPath =  \OC\Files\Filesystem::normalizePath('/' . \OCP\User::getUser() . '/files/' . $oldPath);
0 ignored issues
show
Deprecated Code introduced by
The method OCP\User::getUser() has been deprecated with message: 8.0.0 Use \OC::$server->getUserSession()->getUser()->getUID()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
208
209
		$mountManager = \OC\Files\Filesystem::getMountManager();
210
		$mountedShares = $mountManager->findIn('/' . \OCP\User::getUser() . '/files/' . $oldPath);
0 ignored issues
show
Deprecated Code introduced by
The method OCP\User::getUser() has been deprecated with message: 8.0.0 Use \OC::$server->getUserSession()->getUser()->getUID()

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
211
		foreach ($mountedShares as $mount) {
212
			if ($mount->getStorage()->instanceOfStorage('OCA\Files_Sharing\ISharedStorage')) {
213
				$mountPoint = $mount->getMountPoint();
214
				$target = str_replace($absOldPath, $absNewPath, $mountPoint);
215
				$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: OCA\Files_External\Lib\PersonalMount, OCA\Files_Sharing\External\Mount, OCA\Files_Sharing\SharedMount. 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...
216
			}
217
		}
218
	}
219
220
}
221