null, 'version' => PLATFORM_VERSION, ]; /** * @param Response $response * * @return Response * @throws \Twig\Error\LoaderError * @throws \Twig\Error\RuntimeError * @throws \Twig\Error\SyntaxError */ public function uploadWebPage(Response $response): Response { $maxFileSize = min(stringToBytes(ini_get('post_max_size')), stringToBytes(ini_get('upload_max_filesize'))); return view()->render($response, 'upload/web.twig', [ 'max_file_size' => humanFileSize($maxFileSize), ]); } /** * @param Request $request * @param Response $response * @return Response * @throws Exception */ public function uploadWeb(Request $request, Response $response): Response { try { $file = $this->validateFile($request, $response); $user = make(UserRepository::class)->get($request, $this->session->get('user_id')); $this->validateUser($request, $response, $file, $user); } catch (ValidationException $e) { return $e->response(); } if (!$this->updateUserQuota($request, $user->id, $file->getSize())) { $this->json['message'] = 'User disk quota exceeded.'; return json($response, $this->json, 507); } try { $response = $this->saveMedia($response, $file, $user); $this->setSessionQuotaInfo($user->current_disk_quota + $file->getSize(), $user->max_disk_quota); } catch (Exception $e) { $this->updateUserQuota($request, $user->id, $file->getSize(), true); throw $e; } return $response; } /** * @param Request $request * @param Response $response * * @return Response * @throws Exception */ public function uploadEndpoint(Request $request, Response $response): Response { if ($this->config['maintenance']) { $this->json['message'] = 'Endpoint under maintenance.'; return json($response, $this->json, 503); } try { $file = $this->validateFile($request, $response); if (param($request, 'token') === null) { $this->json['message'] = 'Token not specified.'; return json($response, $this->json, 400); } $user = $this->database->query('SELECT * FROM `users` WHERE `token` = ? LIMIT 1', param($request, 'token'))->fetch(); $this->validateUser($request, $response, $file, $user); } catch (ValidationException $e) { return $e->response(); } if (!$this->updateUserQuota($request, $user->id, $file->getSize())) { $this->json['message'] = 'User disk quota exceeded.'; return json($response, $this->json, 507); } try { $response = $this->saveMedia($response, $file, $user); } catch (Exception $e) { $this->updateUserQuota($request, $user->id, $file->getSize(), true); throw $e; } return $response; } /** * @param Request $request * @param Response $response * @return UploadedFileInterface * @throws ValidationException */ protected function validateFile(Request $request, Response $response) { $iniValue = ini_get('post_max_size'); $maxPostSize = $iniValue === '0' ? INF : stringToBytes($iniValue); if ($request->getServerParams()['CONTENT_LENGTH'] > $maxPostSize) { $this->json['message'] = 'File too large (post_max_size too low?).'; throw new ValidationException(json($response, $this->json, 400)); } $file = array_values($request->getUploadedFiles()); /** @var UploadedFileInterface|null $file */ $file = $file[0] ?? null; if ($file === null) { $this->json['message'] = 'Request without file attached.'; throw new ValidationException(json($response, $this->json, 400)); } if ($file->getError() === UPLOAD_ERR_INI_SIZE) { $this->json['message'] = 'File too large (upload_max_filesize too low?).'; throw new ValidationException(json($response, $this->json, 400)); } return $file; } /** * @param Request $request * @param Response $response * @param UploadedFileInterface $file * @param $user * @return void * @throws ValidationException */ protected function validateUser(Request $request, Response $response, UploadedFileInterface $file, $user) { if (!$user) { $this->json['message'] = 'Token specified not found.'; throw new ValidationException(json($response, $this->json, 404)); } if (!$user->active) { $this->json['message'] = 'Account disabled.'; throw new ValidationException(json($response, $this->json, 401)); } } /** * @param Response $response * @param UploadedFileInterface $file * @param $user * @return Response * @throws \League\Flysystem\FileExistsException * @throws \League\Flysystem\FileNotFoundException */ protected function saveMedia(Response $response, UploadedFileInterface $file, $user) { do { $code = humanRandomString(); } while ($this->database->query('SELECT COUNT(*) AS `count` FROM `uploads` WHERE `code` = ?', $code)->fetch()->count > 0); $fileInfo = pathinfo($file->getClientFilename()); $storagePath = "$user->user_code/$code.$fileInfo[extension]"; $this->storage->writeStream($storagePath, $file->getStream()->detach()); $this->database->query('INSERT INTO `uploads`(`user_id`, `code`, `filename`, `storage_path`, `published`) VALUES (?, ?, ?, ?, ?)', [ $user->id, $code, $file->getClientFilename(), $storagePath, $user->hide_uploads == '1' ? 0 : 1, ]); $mediaId = $this->database->getPdo()->lastInsertId(); if ($this->getSetting('auto_tagging') === 'on') { $this->autoTag($mediaId, $storagePath); } $this->json['message'] = 'OK'; $this->json['url'] = urlFor("/{$user->user_code}/{$code}.{$fileInfo['extension']}"); $this->json['raw_url'] = urlFor("/{$user->user_code}/{$code}/raw.{$fileInfo['extension']}"); $this->logger->info("User $user->username uploaded new media.", [$mediaId]); return json($response, $this->json, 201); } /** * @param $mediaId * @param $storagePath * @throws \League\Flysystem\FileNotFoundException */ protected function autoTag($mediaId, $storagePath) { $mime = $this->storage->getMimetype($storagePath); [$type, $subtype] = explode('/', $mime); /** @var TagRepository $query */ $query = make(TagRepository::class); $query->addTag($type, $mediaId); if ($type === 'application' || $subtype === 'gif') { $query->addTag($subtype, $mediaId); } } }