social/lib/Service/CacheDocumentService.php

280 wiersze
7.5 KiB
PHP

<?php
declare(strict_types=1);
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2018, Maxence Lange <maxence@artificial-owl.com>
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Social\Service;
use OCA\Social\Tools\Exceptions\MalformedArrayException;
use OCA\Social\Tools\Exceptions\RequestContentException;
use OCA\Social\Tools\Exceptions\RequestNetworkException;
use OCA\Social\Tools\Exceptions\RequestResultSizeException;
use OCA\Social\Tools\Exceptions\RequestServerException;
use OCA\Social\Tools\Model\NCRequest;
use OCA\Social\Tools\Model\Request;
use OCA\Social\Tools\Traits\TArrayTools;
use OCA\Social\Tools\Traits\TStringTools;
use Exception;
use Gumlet\ImageResize;
use Gumlet\ImageResizeException;
use OCA\Social\Exceptions\CacheContentException;
use OCA\Social\Exceptions\CacheContentMimeTypeException;
use OCA\Social\Exceptions\CacheDocumentDoesNotExistException;
use OCA\Social\Exceptions\SocialAppConfigException;
use OCA\Social\Exceptions\UnauthorizedFediverseException;
use OCA\Social\Model\ActivityPub\Object\Document;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\ISimpleFile;
class CacheDocumentService {
use TArrayTools;
use TStringTools;
public const RESIZED_WIDTH = 280;
public const RESIZED_HEIGHT = 180;
private IAppData $appData;
private CurlService $curlService;
private ConfigService $configService;
private MiscService $miscService;
public function __construct(
IAppData $appData, CurlService $curlService, ConfigService $configService,
MiscService $miscService
) {
$this->appData = $appData;
$this->curlService = $curlService;
$this->configService = $configService;
$this->miscService = $miscService;
}
/**
* @brief Save the local upload to the cache
*
* @throws CacheContentMimeTypeException
* @throws NotFoundException
* @throws NotPermittedException
*/
public function saveLocalUploadToCache(Document $document, string $uploaded, string &$mime = '') {
$content = $uploaded;
$this->saveContentToCache($document, $content, $mime);
}
/**
* @param Document $document
* @param string $mime
*
* @throws CacheContentMimeTypeException
* @throws MalformedArrayException
* @throws NotFoundException
* @throws NotPermittedException
* @throws RequestContentException
* @throws RequestNetworkException
* @throws RequestResultSizeException
* @throws RequestServerException
* @throws SocialAppConfigException
* @throws UnauthorizedFediverseException
*/
public function saveRemoteFileToCache(Document $document, string &$mime = '') {
$content = $this->retrieveContent($document->getUrl());
$this->saveContentToCache($document, $content, $mime);
}
/**
* @param Document $document
* @param string $content
* @param string $mime
*
* @throws CacheContentMimeTypeException
* @throws NotFoundException
* @throws NotPermittedException
*/
public function saveContentToCache(Document $document, string $content, string &$mime = '') {
// To get the mime type, we create a temp file
$tmpFile = tmpfile();
$tmpPath = stream_get_meta_data($tmpFile)['uri'];
fwrite($tmpFile, $content);
$mime = mime_content_type($tmpPath);
fclose($tmpFile);
$this->filterMimeTypes($mime);
$filename = $this->generateFileFromContent($content);
$document->setLocalCopy($filename);
$this->resizeImage($content);
$resized = $this->generateFileFromContent($content);
$document->setResizedCopy($resized);
}
public function saveFromTempToCache(Document $document, string $tmpPath) {
$mime = mime_content_type($tmpPath);
$this->filterMimeTypes($mime);
$document->setMediaType($mime);
$document->setMimeType($mime);
$file = fopen($tmpPath, 'r');
$content = fread($file, filesize($tmpPath));
$filename = $this->generateFileFromContent($content);
$document->setLocalCopy($filename);
$this->resizeImage($content);
$resized = $this->generateFileFromContent($content);
$document->setResizedCopy($resized);
}
/**
* @param string $content
*
* @return string
* @throws NotPermittedException
* @throws NotFoundException
*/
private function generateFileFromContent(string $content): string {
$filename = $this->uuid();
// creating a path aa/bb/cc/dd/ from the filename aabbccdd-0123-[...]
$path = chunk_split(substr($filename, 0, 8), 2, '/');
try {
$folder = $this->appData->getFolder($path);
} catch (NotFoundException $e) {
$folder = $this->appData->newFolder($path);
}
$cache = $folder->newFile($filename);
$cache->putContent($content);
return $path . $filename;
}
/**
*
* @param string $mime
*
* @throws CacheContentMimeTypeException
*/
public function filterMimeTypes(string $mime) {
$allowedMimeType = [
'image/jpeg',
'image/gif',
'image/png'
];
if (in_array($mime, $allowedMimeType)) {
return;
}
throw new CacheContentMimeTypeException();
}
/**
* @param string $content
*/
private function resizeImage(string &$content) {
try {
$image = ImageResize::createFromString($content);
$image->quality_jpg = 100;
$image->quality_png = 9;
$image->resizeToBestFit(self::RESIZED_WIDTH, self::RESIZED_HEIGHT);
$newContent = $image->getImageAsString();
if (!$newContent) {
$content = $newContent;
}
} catch (ImageResizeException $e) {
}
}
/**
* @param string $path
*
* @return ISimpleFile
* @throws CacheContentException
* @throws CacheDocumentDoesNotExistException
*/
public function getContentFromCache(string $path): ISimpleFile {
if ($path === '') {
throw new CacheDocumentDoesNotExistException();
}
// right now, we do not handle cache for local avatar, we need to change this
// so the current avatar is cached, or a new avatar is uploaded
if ($path === 'avatar') {
throw new CacheContentException();
}
$pos = strrpos($path, '/');
$dir = substr($path, 0, $pos);
$filename = substr($path, $pos + 1);
try {
$file = $this->appData->getFolder($dir)
->getFile($filename);
return $file;
} catch (Exception $e) {
throw new CacheContentException();
}
}
/**
* @param string $url
*
* @return string
* @throws MalformedArrayException
* @throws RequestContentException
* @throws RequestNetworkException
* @throws RequestResultSizeException
* @throws RequestServerException
* @throws SocialAppConfigException
* @throws UnauthorizedFediverseException
*/
public function retrieveContent(string $url): string {
$url = parse_url($url);
$this->mustContains(['path', 'host', 'scheme'], $url);
$request = new NCRequest($url['path'], Request::TYPE_GET, true);
$request->setHost($url['host']);
$request->setProtocol($url['scheme']);
return $this->curlService->doRequest($request);
}
}