Passed
Push — master ( 72e6ca...592cc0 )
by John
12:30 queued 11s
created

AvatarController::getAvatar()   B

Complexity

Conditions 8
Paths 18

Size

Total Lines 36
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 22
dl 0
loc 36
rs 8.4444
c 1
b 0
f 0
cc 8
nc 18
nop 2
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016, ownCloud, Inc.
4
 *
5
 * @author Joas Schilling <[email protected]>
6
 * @author Lukas Reschke <[email protected]>
7
 * @author Morris Jobke <[email protected]>
8
 * @author Roeland Jago Douma <[email protected]>
9
 * @author Thomas Müller <[email protected]>
10
 * @author Vincent Petry <[email protected]>
11
 * @author John Molakvoæ <[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\Core\Controller;
30
31
use OC\AppFramework\Utility\TimeFactory;
32
use OCP\Accounts\IAccountManager;
33
use OCP\AppFramework\Controller;
34
use OCP\AppFramework\Http;
35
use OCP\AppFramework\Http\DataDisplayResponse;
36
use OCP\AppFramework\Http\FileDisplayResponse;
37
use OCP\AppFramework\Http\JSONResponse;
38
use OCP\Files\File;
39
use OCP\Files\IRootFolder;
40
use OCP\IAvatarManager;
41
use OCP\ICache;
42
use OCP\IL10N;
43
use OCP\ILogger;
44
use OCP\IRequest;
45
use OCP\IUserManager;
46
use OCP\IUserSession;
47
48
/**
49
 * Class AvatarController
50
 *
51
 * @package OC\Core\Controller
52
 */
53
class AvatarController extends Controller {
54
55
	/** @var IAvatarManager */
56
	protected $avatarManager;
57
58
	/** @var ICache */
59
	protected $cache;
60
61
	/** @var IL10N */
62
	protected $l;
63
64
	/** @var IUserManager */
65
	protected $userManager;
66
67
	/** @var IUserSession */
68
	protected $userSession;
69
70
	/** @var IRootFolder */
71
	protected $rootFolder;
72
73
	/** @var ILogger */
74
	protected $logger;
75
76
	/** @var string */
77
	protected $userId;
78
79
	/** @var TimeFactory */
80
	protected $timeFactory;
81
	/** @var IAccountManager */
82
	private $accountManager;
83
84
	public function __construct($appName,
85
								IRequest $request,
86
								IAvatarManager $avatarManager,
87
								ICache $cache,
88
								IL10N $l10n,
89
								IUserManager $userManager,
90
								IRootFolder $rootFolder,
91
								ILogger $logger,
92
								$userId,
93
								TimeFactory $timeFactory,
94
								IAccountManager $accountManager) {
95
		parent::__construct($appName, $request);
96
97
		$this->avatarManager = $avatarManager;
98
		$this->cache = $cache;
99
		$this->l = $l10n;
100
		$this->userManager = $userManager;
101
		$this->rootFolder = $rootFolder;
102
		$this->logger = $logger;
103
		$this->userId = $userId;
104
		$this->timeFactory = $timeFactory;
105
		$this->accountManager = $accountManager;
106
	}
107
108
109
	/**
110
	 * @NoAdminRequired
111
	 * @NoCSRFRequired
112
	 * @NoSameSiteCookieRequired
113
	 * @PublicPage
114
	 *
115
	 * @param string $userId
116
	 * @param int $size
117
	 * @return JSONResponse|FileDisplayResponse
118
	 */
119
	public function getAvatar($userId, $size) {
120
		// min/max size
121
		if ($size > 2048) {
122
			$size = 2048;
123
		} elseif ($size <= 0) {
124
			$size = 64;
125
		}
126
127
		$user = $this->userManager->get($userId);
128
		if ($user === null) {
129
			return new JSONResponse([], Http::STATUS_NOT_FOUND);
130
		}
131
132
		$account = $this->accountManager->getAccount($user);
133
		$scope = $account->getProperty(IAccountManager::PROPERTY_AVATAR)->getScope();
134
135
		if ($scope !== IAccountManager::VISIBILITY_PUBLIC && $this->userId === null) {
136
			// Public avatar access is not allowed
137
			return new JSONResponse([], Http::STATUS_NOT_FOUND);
138
		}
139
140
		try {
141
			$avatar = $this->avatarManager->getAvatar($userId);
142
			$avatarFile = $avatar->getFile($size);
143
			$resp = new FileDisplayResponse(
144
				$avatarFile,
145
				$avatar->isCustomAvatar() ? Http::STATUS_OK : Http::STATUS_CREATED,
146
				['Content-Type' => $avatarFile->getMimeType()]
147
			);
148
		} catch (\Exception $e) {
149
			return new JSONResponse([], Http::STATUS_NOT_FOUND);
150
		}
151
152
		// Cache for 30 minutes
153
		$resp->cacheFor(1800);
154
		return $resp;
155
	}
156
157
	/**
158
	 * @NoAdminRequired
159
	 *
160
	 * @param string $path
161
	 * @return JSONResponse
162
	 */
163
	public function postAvatar($path) {
164
		$files = $this->request->getUploadedFile('files');
165
166
		if (isset($path)) {
167
			$path = stripslashes($path);
168
			$userFolder = $this->rootFolder->getUserFolder($this->userId);
169
			/** @var File $node */
170
			$node = $userFolder->get($path);
171
			if (!($node instanceof File)) {
0 ignored issues
show
introduced by
$node is always a sub-type of OCP\Files\File.
Loading history...
172
				return new JSONResponse(['data' => ['message' => $this->l->t('Please select a file.')]]);
173
			}
174
			if ($node->getSize() > 20*1024*1024) {
175
				return new JSONResponse(
176
					['data' => ['message' => $this->l->t('File is too big')]],
177
					Http::STATUS_BAD_REQUEST
178
				);
179
			}
180
181
			if ($node->getMimeType() !== 'image/jpeg' && $node->getMimeType() !== 'image/png') {
182
				return new JSONResponse(
183
					['data' => ['message' => $this->l->t('The selected file is not an image.')]],
184
					Http::STATUS_BAD_REQUEST
185
				);
186
			}
187
188
			try {
189
				$content = $node->getContent();
190
			} catch (\OCP\Files\NotPermittedException $e) {
191
				return new JSONResponse(
192
					['data' => ['message' => $this->l->t('The selected file cannot be read.')]],
193
					Http::STATUS_BAD_REQUEST
194
				);
195
			}
196
		} elseif (!is_null($files)) {
0 ignored issues
show
introduced by
The condition is_null($files) is always false.
Loading history...
197
			if (
198
				$files['error'][0] === 0 &&
199
				 is_uploaded_file($files['tmp_name'][0]) &&
200
				!\OC\Files\Filesystem::isFileBlacklisted($files['tmp_name'][0])
201
			) {
202
				if ($files['size'][0] > 20*1024*1024) {
203
					return new JSONResponse(
204
						['data' => ['message' => $this->l->t('File is too big')]],
205
						Http::STATUS_BAD_REQUEST
206
					);
207
				}
208
				$this->cache->set('avatar_upload', file_get_contents($files['tmp_name'][0]), 7200);
209
				$content = $this->cache->get('avatar_upload');
210
				unlink($files['tmp_name'][0]);
211
			} else {
212
				return new JSONResponse(
213
					['data' => ['message' => $this->l->t('Invalid file provided')]],
214
					Http::STATUS_BAD_REQUEST
215
				);
216
			}
217
		} else {
218
			//Add imgfile
219
			return new JSONResponse(
220
				['data' => ['message' => $this->l->t('No image or file provided')]],
221
				Http::STATUS_BAD_REQUEST
222
			);
223
		}
224
225
		try {
226
			$image = new \OC_Image();
227
			$image->loadFromData($content);
228
			$image->readExif($content);
229
			$image->fixOrientation();
230
231
			if ($image->valid()) {
232
				$mimeType = $image->mimeType();
233
				if ($mimeType !== 'image/jpeg' && $mimeType !== 'image/png') {
234
					return new JSONResponse(
235
						['data' => ['message' => $this->l->t('Unknown filetype')]],
236
						Http::STATUS_OK
237
					);
238
				}
239
240
				$this->cache->set('tmpAvatar', $image->data(), 7200);
241
				return new JSONResponse(
242
					['data' => 'notsquare'],
243
					Http::STATUS_OK
244
				);
245
			} else {
246
				return new JSONResponse(
247
					['data' => ['message' => $this->l->t('Invalid image')]],
248
					Http::STATUS_OK
249
				);
250
			}
251
		} catch (\Exception $e) {
252
			$this->logger->logException($e, ['app' => 'core']);
253
			return new JSONResponse(['data' => ['message' => $this->l->t('An error occurred. Please contact your admin.')]], Http::STATUS_OK);
254
		}
255
	}
256
257
	/**
258
	 * @NoAdminRequired
259
     *
260
	 * @return JSONResponse
261
	 */
262
	public function deleteAvatar() {
263
		try {
264
			$avatar = $this->avatarManager->getAvatar($this->userId);
265
			$avatar->remove();
266
			return new JSONResponse();
267
		} catch (\Exception $e) {
268
			$this->logger->logException($e, ['app' => 'core']);
269
			return new JSONResponse(['data' => ['message' => $this->l->t('An error occurred. Please contact your admin.')]], Http::STATUS_BAD_REQUEST);
270
		}
271
	}
272
273
	/**
274
	 * @NoAdminRequired
275
	 *
276
	 * @return JSONResponse|DataDisplayResponse
277
	 */
278
	public function getTmpAvatar() {
279
		$tmpAvatar = $this->cache->get('tmpAvatar');
280
		if (is_null($tmpAvatar)) {
281
			return new JSONResponse(['data' => [
282
										'message' => $this->l->t("No temporary profile picture available, try again")
283
									]],
284
									Http::STATUS_NOT_FOUND);
285
		}
286
287
		$image = new \OC_Image();
288
		$image->loadFromData($tmpAvatar);
289
290
		$resp = new DataDisplayResponse($image->data(),
291
				Http::STATUS_OK,
292
				['Content-Type' => $image->mimeType()]);
293
294
		$resp->setETag((string)crc32($image->data()));
295
		$resp->cacheFor(0);
296
		$resp->setLastModified(new \DateTime('now', new \DateTimeZone('GMT')));
297
		return $resp;
298
	}
299
300
	/**
301
	 * @NoAdminRequired
302
	 *
303
	 * @param array $crop
304
	 * @return JSONResponse
305
	 */
306
	public function postCroppedAvatar($crop) {
307
		if (is_null($crop)) {
0 ignored issues
show
introduced by
The condition is_null($crop) is always false.
Loading history...
308
			return new JSONResponse(['data' => ['message' => $this->l->t("No crop data provided")]],
309
									Http::STATUS_BAD_REQUEST);
310
		}
311
312
		if (!isset($crop['x'], $crop['y'], $crop['w'], $crop['h'])) {
313
			return new JSONResponse(['data' => ['message' => $this->l->t("No valid crop data provided")]],
314
									Http::STATUS_BAD_REQUEST);
315
		}
316
317
		$tmpAvatar = $this->cache->get('tmpAvatar');
318
		if (is_null($tmpAvatar)) {
319
			return new JSONResponse(['data' => [
320
										'message' => $this->l->t("No temporary profile picture available, try again")
321
									]],
322
									Http::STATUS_BAD_REQUEST);
323
		}
324
325
		$image = new \OC_Image();
326
		$image->loadFromData($tmpAvatar);
327
		$image->crop($crop['x'], $crop['y'], (int)round($crop['w']), (int)round($crop['h']));
328
		try {
329
			$avatar = $this->avatarManager->getAvatar($this->userId);
330
			$avatar->set($image);
331
			// Clean up
332
			$this->cache->remove('tmpAvatar');
333
			return new JSONResponse(['status' => 'success']);
334
		} catch (\OC\NotSquareException $e) {
335
			return new JSONResponse(['data' => ['message' => $this->l->t('Crop is not square')]],
336
									Http::STATUS_BAD_REQUEST);
337
		} catch (\Exception $e) {
338
			$this->logger->logException($e, ['app' => 'core']);
339
			return new JSONResponse(['data' => ['message' => $this->l->t('An error occurred. Please contact your admin.')]], Http::STATUS_BAD_REQUEST);
340
		}
341
	}
342
}
343