Completed
Push — stable8.2 ( 7a94a5...a03173 )
by Morris
29:37
created

ShareController   D

Complexity

Total Complexity 37

Size/Duplication

Total Lines 291
Duplicated Lines 3.44 %

Coupling/Cohesion

Components 1
Dependencies 22

Test Coverage

Coverage 61.53%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 37
lcom 1
cbo 22
dl 10
loc 291
ccs 88
cts 143
cp 0.6153
rs 4.9789
c 1
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 19 1
A showAuthenticate() 0 9 2
A authenticate() 0 14 3
C showShare() 4 75 7
B getPath() 0 21 7
C downloadShare() 6 79 17

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/**
3
 * @author Björn Schießle <[email protected]>
4
 * @author Georg Ehrke <[email protected]>
5
 * @author Joas Schilling <[email protected]>
6
 * @author Lukas Reschke <[email protected]>
7
 * @author Morris Jobke <[email protected]>
8
 * @author Robin Appelman <[email protected]>
9
 * @author Robin McCorkell <[email protected]>
10
 *
11
 * @copyright Copyright (c) 2015, ownCloud, Inc.
12
 * @license AGPL-3.0
13
 *
14
 * This code is free software: you can redistribute it and/or modify
15
 * it under the terms of the GNU Affero General Public License, version 3,
16
 * as published by the Free Software Foundation.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21
 * GNU Affero General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU Affero General Public License, version 3,
24
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
25
 *
26
 */
27
28
namespace OCA\Files_Sharing\Controllers;
29
30
use OC;
31
use OC\Files\Filesystem;
32
use OC_Files;
33
use OC_Util;
34
use OCP;
35
use OCP\Template;
36
use OCP\Share;
37
use OCP\AppFramework\Controller;
38
use OCP\IRequest;
39
use OCP\AppFramework\Http\TemplateResponse;
40
use OCP\AppFramework\Http\RedirectResponse;
41
use OCP\AppFramework\Http\NotFoundResponse;
42
use OC\URLGenerator;
43
use OC\AppConfig;
44
use OCP\ILogger;
45
use OCA\Files_Sharing\Helper;
46
use OCP\User;
47
use OCP\Util;
48
use OCA\Files_Sharing\Activity;
49
use \OCP\Files\NotFoundException;
50
51
/**
52
 * Class ShareController
53
 *
54
 * @package OCA\Files_Sharing\Controllers
55
 */
56
class ShareController extends Controller {
57
58
	/** @var \OC\User\Session */
59
	protected $userSession;
60
	/** @var \OC\AppConfig */
61
	protected $appConfig;
62
	/** @var \OCP\IConfig */
63
	protected $config;
64
	/** @var \OC\URLGenerator */
65
	protected $urlGenerator;
66
	/** @var \OC\User\Manager */
67
	protected $userManager;
68
	/** @var \OCP\ILogger */
69
	protected $logger;
70
	/** @var OCP\Activity\IManager */
71
	protected $activityManager;
72
73
	/**
74
	 * @param string $appName
75
	 * @param IRequest $request
76
	 * @param OC\User\Session $userSession
77
	 * @param AppConfig $appConfig
78
	 * @param OCP\IConfig $config
79
	 * @param URLGenerator $urlGenerator
80
	 * @param OCP\IUserManager $userManager
81
	 * @param ILogger $logger
82
	 * @param OCP\Activity\IManager $activityManager
83
	 */
84 7
	public function __construct($appName,
85
								IRequest $request,
86
								OC\User\Session $userSession,
87
								AppConfig $appConfig,
88
								OCP\IConfig $config,
89
								URLGenerator $urlGenerator,
90
								OCP\IUserManager $userManager,
91
								ILogger $logger,
92
								OCP\Activity\IManager $activityManager) {
93 7
		parent::__construct($appName, $request);
94
95 7
		$this->userSession = $userSession;
96 7
		$this->appConfig = $appConfig;
97 7
		$this->config = $config;
98 7
		$this->urlGenerator = $urlGenerator;
99 7
		$this->userManager = $userManager;
0 ignored issues
show
Documentation Bug introduced by
$userManager is of type object<OCP\IUserManager>, but the property $userManager was declared to be of type object<OC\User\Manager>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
100 7
		$this->logger = $logger;
101 7
		$this->activityManager = $activityManager;
102 7
	}
103
104
	/**
105
	 * @PublicPage
106
	 * @NoCSRFRequired
107
	 *
108
	 * @param string $token
109
	 * @return TemplateResponse|RedirectResponse
110
	 */
111 1
	public function showAuthenticate($token) {
112 1
		$linkItem = Share::getShareByToken($token, false);
113
114 1
		if(Helper::authenticate($linkItem)) {
0 ignored issues
show
Bug introduced by
It seems like $linkItem defined by \OCP\Share::getShareByToken($token, false) on line 112 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...
115 1
			return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.showShare', array('token' => $token)));
116
		}
117
118 1
		return new TemplateResponse($this->appName, 'authenticate', array(), 'guest');
119
	}
120
121
	/**
122
	 * @PublicPage
123
	 * @UseSession
124
	 *
125
	 * Authenticates against password-protected shares
126
	 * @param $token
127
	 * @param string $password
128
	 * @return RedirectResponse|TemplateResponse
129
	 */
130 1
	public function authenticate($token, $password = '') {
131 1
		$linkItem = Share::getShareByToken($token, false);
132 1
		if($linkItem === false) {
133 1
			return new NotFoundResponse();
134
		}
135
136 1
		$authenticate = Helper::authenticate($linkItem, $password);
0 ignored issues
show
Bug introduced by
It seems like $linkItem defined by \OCP\Share::getShareByToken($token, false) on line 131 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...
137
138 1
		if($authenticate === true) {
139 1
			return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.showShare', array('token' => $token)));
140
		}
141
142 1
		return new TemplateResponse($this->appName, 'authenticate', array('wrongpw' => true), 'guest');
143
	}
144
145
	/**
146
	 * @PublicPage
147
	 * @NoCSRFRequired
148
	 *
149
	 * @param string $token
150
	 * @param string $path
151
	 * @return TemplateResponse|RedirectResponse
152
	 * @throws NotFoundException
153
	 */
154 3
	public function showShare($token, $path = '') {
155 3
		\OC_User::setIncognitoMode(true);
156
157
		// Check whether share exists
158 3
		$linkItem = Share::getShareByToken($token, false);
159 3
		if($linkItem === false) {
160 1
			return new NotFoundResponse();
161
		}
162
163 3
		$shareOwner = $linkItem['uid_owner'];
164 3
		$originalSharePath = $this->getPath($token);
165
166
		// Share is password protected - check whether the user is permitted to access the share
167 1 View Code Duplication
		if (isset($linkItem['share_with']) && !Helper::authenticate($linkItem)) {
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...
Bug introduced by
It seems like $linkItem defined by \OCP\Share::getShareByToken($token, false) on line 158 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...
168 1
			return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.authenticate',
169 1
				array('token' => $token)));
170
		}
171
172 1
		if (Filesystem::isReadable($originalSharePath . $path)) {
173 1
			$getPath = Filesystem::normalizePath($path);
174 1
			$originalSharePath .= $path;
175 1
		} else {
176
			throw new NotFoundException();
177
		}
178
179 1
		$file = basename($originalSharePath);
180
181 1
		$shareTmpl = [];
182 1
		$shareTmpl['displayName'] = User::getDisplayName($shareOwner);
183 1
		$shareTmpl['owner'] = $shareOwner;
184 1
		$shareTmpl['filename'] = $file;
185 1
		$shareTmpl['directory_path'] = $linkItem['file_target'];
186 1
		$shareTmpl['mimetype'] = Filesystem::getMimeType($originalSharePath);
187 1
		$shareTmpl['previewSupported'] = \OC::$server->getPreviewManager()->isMimeSupported($shareTmpl['mimetype']);
188 1
		$shareTmpl['dirToken'] = $linkItem['token'];
189 1
		$shareTmpl['sharingToken'] = $token;
190 1
		$shareTmpl['server2serversharing'] = Helper::isOutgoingServer2serverShareEnabled();
191 1
		$shareTmpl['protected'] = isset($linkItem['share_with']) ? 'true' : 'false';
192 1
		$shareTmpl['dir'] = '';
193 1
		$nonHumanFileSize = \OC\Files\Filesystem::filesize($originalSharePath);
194 1
		$shareTmpl['nonHumanFileSize'] = $nonHumanFileSize;
195 1
		$shareTmpl['fileSize'] = \OCP\Util::humanFileSize($nonHumanFileSize);
196
197
		// Show file list
198 1
		if (Filesystem::is_dir($originalSharePath)) {
199
			$shareTmpl['dir'] = $getPath;
200
			$maxUploadFilesize = Util::maxUploadFilesize($originalSharePath);
201
			$freeSpace = Util::freeSpace($originalSharePath);
202
			$uploadLimit = Util::uploadLimit();
203
			$folder = new Template('files', 'list', '');
204
			$folder->assign('dir', $getPath);
205
			$folder->assign('dirToken', $linkItem['token']);
206
			$folder->assign('permissions', \OCP\Constants::PERMISSION_READ);
207
			$folder->assign('isPublic', true);
208
			$folder->assign('publicUploadEnabled', 'no');
209
			$folder->assign('uploadMaxFilesize', $maxUploadFilesize);
210
			$folder->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize));
211
			$folder->assign('freeSpace', $freeSpace);
212
			$folder->assign('uploadLimit', $uploadLimit); // PHP upload limit
213
			$folder->assign('usedSpacePercent', 0);
214
			$folder->assign('trash', false);
215
			$shareTmpl['folder'] = $folder->fetchPage();
216
		}
217
218 1
		$shareTmpl['downloadURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.downloadShare', array('token' => $token));
219 1
		$shareTmpl['maxSizeAnimateGif'] = $this->config->getSystemValue('max_filesize_animated_gifs_public_sharing', 10);
220 1
		$shareTmpl['previewEnabled'] = $this->config->getSystemValue('enable_previews', true);
221
222 1
		$csp = new OCP\AppFramework\Http\ContentSecurityPolicy();
223 1
		$csp->addAllowedFrameDomain('\'self\'');
224 1
		$response = new TemplateResponse($this->appName, 'public', $shareTmpl, 'base');
225 1
		$response->setContentSecurityPolicy($csp);
226
227 1
		return $response;
228
	}
229
230
	/**
231
	 * @PublicPage
232
	 * @NoCSRFRequired
233
	 *
234
	 * @param string $token
235
	 * @param string $files
236
	 * @param string $path
237
	 * @param string $downloadStartSecret
238
	 * @return void|RedirectResponse
239
	 */
240 2
	public function downloadShare($token, $files = null, $path = '', $downloadStartSecret = '') {
241 2
		\OC_User::setIncognitoMode(true);
242
243 2
		$linkItem = OCP\Share::getShareByToken($token, false);
244
245
		// Share is password protected - check whether the user is permitted to access the share
246 2 View Code Duplication
		if (isset($linkItem['share_with'])) {
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...
247 2
			if(!Helper::authenticate($linkItem)) {
0 ignored issues
show
Bug introduced by
It seems like $linkItem defined by \OCP\Share::getShareByToken($token, false) on line 243 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...
248 1
				return new RedirectResponse($this->urlGenerator->linkToRoute('files_sharing.sharecontroller.authenticate',
249 1
					array('token' => $token)));
250
			}
251 1
		}
252
253 1
		$files_list = null;
254 1
		if (!is_null($files)) { // download selected files
255
			$files_list = json_decode($files);
256
			// in case we get only a single file
257
			if ($files_list === null) {
258
				$files_list = array($files);
259
			}
260
		}
261
262 1
		$originalSharePath = self::getPath($token);
263
264
		// Create the activities
265
		if (isset($originalSharePath) && Filesystem::isReadable($originalSharePath . $path)) {
266
			$originalSharePath = Filesystem::normalizePath($originalSharePath . $path);
267
			$isDir = \OC\Files\Filesystem::is_dir($originalSharePath);
268
269
			$activities = [];
270
			if (!$isDir) {
271
				// Single file public share
272
				$activities[$originalSharePath] = Activity::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
273
			} else if (!empty($files_list)) {
274
				// Only some files are downloaded
275
				foreach ($files_list as $file) {
276
					$filePath = Filesystem::normalizePath($originalSharePath . '/' . $file);
277
					$isDir = \OC\Files\Filesystem::is_dir($filePath);
278
					$activities[$filePath] = ($isDir) ? Activity::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED : Activity::SUBJECT_PUBLIC_SHARED_FILE_DOWNLOADED;
279
				}
280
			} else {
281
				// The folder is downloaded
282
				$activities[$originalSharePath] = Activity::SUBJECT_PUBLIC_SHARED_FOLDER_DOWNLOADED;
283
			}
284
285
			foreach ($activities as $filePath => $subject) {
286
				$this->activityManager->publishActivity(
287
					'files_sharing', $subject, array($filePath), '', array(),
288
					$filePath, '', $linkItem['uid_owner'], Activity::TYPE_PUBLIC_LINKS, Activity::PRIORITY_MEDIUM
289
				);
290
			}
291
		}
292
293
		/**
294
		 * this sets a cookie to be able to recognize the start of the download
295
		 * the content must not be longer than 32 characters and must only contain
296
		 * alphanumeric characters
297
		 */
298
		if (!empty($downloadStartSecret)
299
			&& !isset($downloadStartSecret[32])
300
			&& preg_match('!^[a-zA-Z0-9]+$!', $downloadStartSecret) === 1) {
301
302
			// FIXME: set on the response once we use an actual app framework response
303
			setcookie('ocDownloadStarted', $downloadStartSecret, time() + 20, '/');
304
		}
305
306
		// download selected files
307
		if (!is_null($files) && $files !== '') {
308
			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
309
			// after dispatching the request which results in a "Cannot modify header information" notice.
310
			OC_Files::get($originalSharePath, $files_list, $_SERVER['REQUEST_METHOD'] == 'HEAD');
311
			exit();
312
		} else {
313
			// FIXME: The exit is required here because otherwise the AppFramework is trying to add headers as well
314
			// after dispatching the request which results in a "Cannot modify header information" notice.
315
			OC_Files::get(dirname($originalSharePath), basename($originalSharePath), $_SERVER['REQUEST_METHOD'] == 'HEAD');
316
			exit();
317
		}
318
	}
319
320
	/**
321
	 * @param string $token
322
	 * @return string Resolved file path of the token
323
	 * @throws NotFoundException In case share could not get properly resolved
324
	 */
325 4
	private function getPath($token) {
326 4
		$linkItem = Share::getShareByToken($token, false);
327 4
		if (is_array($linkItem) && isset($linkItem['uid_owner'])) {
328
			// seems to be a valid share
329 4
			$rootLinkItem = Share::resolveReShare($linkItem);
330 4
			if (isset($rootLinkItem['uid_owner'])) {
331 4
				if(!$this->userManager->userExists($rootLinkItem['uid_owner'])) {
332 1
					throw new NotFoundException('Owner of the share does not exist anymore');
333
				}
334 3
				OC_Util::tearDownFS();
335 3
				OC_Util::setupFS($rootLinkItem['uid_owner']);
336 3
				$path = Filesystem::getPath($linkItem['file_source']);
337
338 3
				if(!empty($path) && Filesystem::isReadable($path)) {
339 1
					return $path;
340
				}
341 2
			}
342 2
		}
343
344 2
		throw new NotFoundException('No file found belonging to file.');
345
	}
346
}
347