Completed
Pull Request — master (#32155)
by Thomas
26:27 queued 08:44
created

Helper::getUser()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 4
nop 0
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Bart Visscher <[email protected]>
4
 * @author Björn Schießle <[email protected]>
5
 * @author Joas Schilling <[email protected]>
6
 * @author Jörn Friedrich Dreyer <[email protected]>
7
 * @author Lukas Reschke <[email protected]>
8
 * @author Morris Jobke <[email protected]>
9
 * @author Robin Appelman <[email protected]>
10
 * @author Roeland Jago Douma <[email protected]>
11
 * @author Thomas Müller <[email protected]>
12
 * @author Torben Dannhauer <[email protected]>
13
 * @author Vincent Petry <[email protected]>
14
 *
15
 * @copyright Copyright (c) 2018, ownCloud GmbH
16
 * @license AGPL-3.0
17
 *
18
 * This code is free software: you can redistribute it and/or modify
19
 * it under the terms of the GNU Affero General Public License, version 3,
20
 * as published by the Free Software Foundation.
21
 *
22
 * This program is distributed in the hope that it will be useful,
23
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25
 * GNU Affero General Public License for more details.
26
 *
27
 * You should have received a copy of the GNU Affero General Public License, version 3,
28
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
29
 *
30
 */
31
namespace OCA\Files_Sharing;
32
33
use OC\Files\Filesystem;
34
use OC\Files\View;
35
use OCP\Files\NotFoundException;
36
use OCP\User;
37
38
class Helper {
39
	public static function registerHooks() {
40
		\OCP\Util::connectHook('OC_Filesystem', 'post_rename', '\OCA\Files_Sharing\Updater', 'renameHook');
41
		\OCP\Util::connectHook('OC_Filesystem', 'post_delete', '\OCA\Files_Sharing\Hooks', 'unshareChildren');
42
43
		\OCP\Util::connectHook('OC_User', 'post_deleteUser', '\OCA\Files_Sharing\Hooks', 'deleteUser');
44
	}
45
46
	/**
47
	 * Sets up the filesystem and user for public sharing
48
	 * @param string $token string share token
49
	 * @param string $relativePath optional path relative to the share
50
	 * @param string $password optional password
51
	 * @return array
52
	 */
53
	public static function setupFromToken($token, $relativePath = null, $password = null) {
54
		\OC_User::setIncognitoMode(true);
55
56
		$linkItem = \OCP\Share::getShareByToken($token, !$password);
57
		if ($linkItem === false || ($linkItem['item_type'] !== 'file' && $linkItem['item_type'] !== 'folder')) {
58
			\OC_Response::setStatus(404);
59
			\OCP\Util::writeLog('core-preview', 'Passed token parameter is not valid', \OCP\Util::DEBUG);
60
			exit;
61
		}
62
63
		if (!isset($linkItem['uid_owner']) || !isset($linkItem['file_source'])) {
64
			\OC_Response::setStatus(500);
65
			\OCP\Util::writeLog('core-preview', 'Passed token seems to be valid, but it does not contain all necessary information . ("' . $token . '")', \OCP\Util::WARN);
66
			exit;
67
		}
68
69
		$rootLinkItem = \OCP\Share::resolveReShare($linkItem);
0 ignored issues
show
Bug introduced by
It seems like $linkItem defined by \OCP\Share::getShareByToken($token, !$password) on line 56 can also be of type boolean; however, OCP\Share::resolveReShare() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
70
		$path = null;
71
		if (isset($rootLinkItem['uid_owner'])) {
72
			\OCP\JSON::checkUserExists($rootLinkItem['uid_owner']);
73
			\OC_Util::tearDownFS();
74
			\OC_Util::setupFS($rootLinkItem['uid_owner']);
75
		}
76
77
		try {
78
			$path = Filesystem::getPath($linkItem['file_source']);
79
		} catch (NotFoundException $e) {
80
			\OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG);
81
			\OC_Response::setStatus(404);
82
			\OCP\JSON::error(['success' => false]);
83
			exit();
84
		}
85
86
		if (!isset($linkItem['item_type'])) {
87
			\OCP\Util::writeLog('share', 'No item type set for share id: ' . $linkItem['id'], \OCP\Util::ERROR);
88
			\OC_Response::setStatus(404);
89
			\OCP\JSON::error(['success' => false]);
90
			exit();
91
		}
92
93
		if (isset($linkItem['share_with']) && (int)$linkItem['share_type'] === \OCP\Share::SHARE_TYPE_LINK) {
94
			if (!self::authenticate($linkItem, $password)) {
0 ignored issues
show
Bug introduced by
It seems like $linkItem defined by \OCP\Share::getShareByToken($token, !$password) on line 56 can also be of type boolean; however, OCA\Files_Sharing\Helper::authenticate() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
95
				\OC_Response::setStatus(403);
96
				\OCP\JSON::error(['success' => false]);
97
				exit();
98
			}
99
		}
100
101
		$basePath = $path;
102
103
		if ($relativePath !== null && Filesystem::isReadable($basePath . $relativePath)) {
104
			$path .= Filesystem::normalizePath($relativePath);
105
		}
106
107
		return [
108
			'linkItem' => $linkItem,
109
			'basePath' => $basePath,
110
			'realPath' => $path
111
		];
112
	}
113
114
	/**
115
	 * Authenticate link item with the given password
116
	 * or with the session if no password was given.
117
	 * @param array $linkItem link item array
118
	 * @param string $password optional password
119
	 *
120
	 * @return boolean true if authorized, false otherwise
121
	 */
122
	public static function authenticate($linkItem, $password = null) {
123
		if ($password !== null) {
124
			if ($linkItem['share_type'] == \OCP\Share::SHARE_TYPE_LINK) {
125
				// Check Password
126
				$newHash = '';
127
				if (\OC::$server->getHasher()->verify($password, $linkItem['share_with'], $newHash)) {
128
					// Save item id in session for future requests
129
					\OC::$server->getSession()->set('public_link_authenticated', (string) $linkItem['id']);
130
131
					/**
132
					 * FIXME: Migrate old hashes to new hash format
133
					 * Due to the fact that there is no reasonable functionality to update the password
134
					 * of an existing share no migration is yet performed there.
135
					 * The only possibility is to update the existing share which will result in a new
136
					 * share ID and is a major hack.
137
					 *
138
					 * In the future the migration should be performed once there is a proper method
139
					 * to update the share's password. (for example `$share->updatePassword($password)`
140
					 *
141
					 * @link https://github.com/owncloud/core/issues/10671
142
					 */
143
					if (!empty($newHash)) {
144
					}
145
				} else {
146
					return false;
147
				}
148
			} else {
149
				\OCP\Util::writeLog('share', 'Unknown share type '.$linkItem['share_type']
150
					.' for share id '.$linkItem['id'], \OCP\Util::ERROR);
151
				return false;
152
			}
153 View Code Duplication
		} else {
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...
154
			// not authenticated ?
155
			if (! \OC::$server->getSession()->exists('public_link_authenticated')
156
				|| \OC::$server->getSession()->get('public_link_authenticated') !== (string)$linkItem['id']) {
157
				return false;
158
			}
159
		}
160
		return true;
161
	}
162
163
	public static function getSharesFromItem($target) {
164
		$result = [];
165
		$owner = Filesystem::getOwner($target);
166
		Filesystem::initMountPoints($owner);
167
		$info = Filesystem::getFileInfo($target);
168
		$ownerView = new View('/'.$owner.'/files');
169
		if ($owner != self::getUser()) {
170
			$path = $ownerView->getPath($info['fileid']);
171
		} else {
172
			$path = $target;
173
		}
174
175
		$ids = [];
176
		while ($path !== \dirname($path)) {
177
			$info = $ownerView->getFileInfo($path);
178
			if ($info instanceof \OC\Files\FileInfo) {
179
				$ids[] = $info['fileid'];
180
			} else {
181
				\OCP\Util::writeLog('sharing', 'No fileinfo available for: ' . $path, \OCP\Util::WARN);
182
			}
183
			$path = \dirname($path);
184
		}
185
186
		if (!empty($ids)) {
187
			$idList = \array_chunk($ids, 99, true);
188
189
			foreach ($idList as $subList) {
190
				$statement = "SELECT `share_with`, `share_type`, `file_target` FROM `*PREFIX*share` WHERE `file_source` IN (" . \implode(',', $subList) . ") AND `share_type` IN (0, 1, 2)";
191
				$query = \OCP\DB::prepare($statement);
192
				$r = $query->execute();
193
				$result = \array_merge($result, $r->fetchAll());
194
			}
195
		}
196
197
		return $result;
198
	}
199
200
	/**
201
	 * get the UID of the owner of the file and the path to the file relative to
202
	 * owners files folder
203
	 *
204
	 * @param $filename
205
	 * @return array
206
	 * @throws \OC\User\NoUserException
207
	 */
208
	public static function getUidAndFilename($filename) {
209
		$uid = Filesystem::getOwner($filename);
210
		$userManager = \OC::$server->getUserManager();
211
		// if the user with the UID doesn't exists, e.g. because the UID points
212
		// to a remote user with a federated cloud ID we use the current logged-in
213
		// user. We need a valid local user to create the share
214
		if (!$userManager->userExists($uid)) {
215
			$uid = self::getUser();
216
		}
217
		Filesystem::initMountPoints($uid);
218
		if ($uid != self::getUser()) {
219
			$info = Filesystem::getFileInfo($filename);
220
			$ownerView = new View('/'.$uid.'/files');
221
			try {
222
				$filename = $ownerView->getPath($info['fileid']);
223
			} catch (NotFoundException $e) {
224
				$filename = null;
225
			}
226
		}
227
		return [$uid, $filename];
228
	}
229
230
	/**
231
	 * Format a path to be relative to the /user/files/ directory
232
	 * @param string $path the absolute path
233
	 * @return string e.g. turns '/admin/files/test.txt' into 'test.txt'
234
	 */
235
	public static function stripUserFilesPath($path) {
236
		$trimmed = \ltrim($path, '/');
237
		$split = \explode('/', $trimmed);
238
239
		// it is not a file relative to data/user/files
240
		if (\count($split) < 3 || $split[1] !== 'files') {
241
			return false;
242
		}
243
244
		$sliced = \array_slice($split, 2);
245
		$relPath = \implode('/', $sliced);
246
247
		return $relPath;
248
	}
249
250
	/**
251
	 * check if file name already exists and generate unique target
252
	 *
253
	 * @param string $path
254
	 * @param array $excludeList
255
	 * @param View $view
256
	 * @return string $path
257
	 */
258
	public static function generateUniqueTarget($path, $excludeList, $view) {
259
		$pathinfo = \pathinfo($path);
260
		$ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : '';
261
		$name = $pathinfo['filename'];
262
		$dir = $pathinfo['dirname'];
263
		$i = 2;
264 View Code Duplication
		while ($view->file_exists($path) || \in_array($path, $excludeList)) {
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...
265
			$path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext);
266
			$i++;
267
		}
268
269
		return $path;
270
	}
271
272
	/**
273
	 * get default share folder
274
	 *
275
	 * @param \OC\Files\View
276
	 * @return string
277
	 */
278
	public static function getShareFolder($view = null) {
279
		if ($view === null) {
280
			$view = Filesystem::getView();
281
		}
282
		$shareFolder = \OC::$server->getConfig()->getSystemValue('share_folder', '/');
283
		$shareFolder = Filesystem::normalizePath($shareFolder);
284
285 View Code Duplication
		if (!$view->file_exists($shareFolder)) {
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...
286
			$dir = '';
287
			$subdirs = \explode('/', $shareFolder);
288
			foreach ($subdirs as $subdir) {
289
				$dir = $dir . '/' . $subdir;
290
				if (!$view->is_dir($dir)) {
291
					$view->mkdir($dir);
292
				}
293
			}
294
		}
295
296
		return $shareFolder;
297
	}
298
299
	/**
300
	 * set default share folder
301
	 *
302
	 * @param string $shareFolder
303
	 */
304
	public static function setShareFolder($shareFolder) {
305
		\OC::$server->getConfig()->setSystemValue('share_folder', $shareFolder);
306
	}
307
308
	public static function getUser() {
309
		$user = \OC::$server->getUserSession() ? \OC::$server->getUserSession()->getUser() : null;
310
		if ($user === null) {
311
			return null;
312
		}
313
		return $user->getUID();
314
	}
315
}
316