From e519b782fd2180d326ffbd9fc8f1d6fca111ce98 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 4 Jul 2021 21:24:49 +0000 Subject: [PATCH] Simplified proxy handling --- include/api.php | 6 +- src/Content/Text/BBCode.php | 2 +- src/Model/Contact.php | 2 +- src/Module/Proxy.php | 196 ++++-------------------------------- src/Protocol/OStatus.php | 2 +- src/Util/Proxy.php | 18 +--- 6 files changed, 30 insertions(+), 196 deletions(-) diff --git a/include/api.php b/include/api.php index 492d3d1cb7..51e4303327 100644 --- a/include/api.php +++ b/include/api.php @@ -2678,7 +2678,7 @@ function api_get_attachments(&$body) if (DI::config()->get("system", "proxy_disabled")) { $attachments[] = ["url" => $image, "mimetype" => $imagedata["mime"], "size" => $imagedata["size"]]; } else { - $attachments[] = ["url" => ProxyUtils::proxifyUrl($image, false), "mimetype" => $imagedata["mime"], "size" => $imagedata["size"]]; + $attachments[] = ["url" => ProxyUtils::proxifyUrl($image), "mimetype" => $imagedata["mime"], "size" => $imagedata["size"]]; } } } @@ -2703,7 +2703,7 @@ function api_get_entitities(&$text, $bbcode) preg_match_all("/\[img](.*?)\[\/img\]/ism", $bbcode, $images); foreach ($images[1] as $image) { - $replace = ProxyUtils::proxifyUrl($image, false); + $replace = ProxyUtils::proxifyUrl($image); $text = str_replace($image, $replace, $text); } return []; @@ -2818,7 +2818,7 @@ function api_get_entitities(&$text, $bbcode) // If image cache is activated, then use the following sizes: // thumb (150), small (340), medium (600) and large (1024) if (!DI::config()->get("system", "proxy_disabled")) { - $media_url = ProxyUtils::proxifyUrl($url, false); + $media_url = ProxyUtils::proxifyUrl($url); $sizes = []; $scale = Images::getScalingDimensions($image[0], $image[1], 150); diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index 634ea9aac0..3ecd306b3f 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -1012,7 +1012,7 @@ class BBCode if (!empty($author_contact['id'])) { $attributes['avatar'] = Contact::getAvatarUrlForId($author_contact['id'], ProxyUtils::SIZE_THUMB); } elseif ($attributes['avatar']) { - $attributes['avatar'] = ProxyUtils::proxifyUrl($attributes['avatar'], false, ProxyUtils::SIZE_THUMB); + $attributes['avatar'] = ProxyUtils::proxifyUrl($attributes['avatar'], ProxyUtils::SIZE_THUMB); } $content = preg_replace(Strings::autoLinkRegEx(), '$1', $match[3]); diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 1679fc25c5..5fc234d609 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1517,7 +1517,7 @@ class Contact if (Proxy::isLocalImage($avatar)) { return $avatar; } else { - return Proxy::proxifyUrl($avatar, false, $size); + return Proxy::proxifyUrl($avatar, $size); } } diff --git a/src/Module/Proxy.php b/src/Module/Proxy.php index 317ec530a5..6ccc38f60d 100644 --- a/src/Module/Proxy.php +++ b/src/Module/Proxy.php @@ -24,10 +24,9 @@ namespace Friendica\Module; use Friendica\BaseModule; use Friendica\Core\Logger; use Friendica\Core\System; -use Friendica\DI; -use Friendica\Model\Photo; use Friendica\Object\Image; use Friendica\Util\HTTPSignature; +use Friendica\Util\Images; use Friendica\Util\Proxy as ProxyUtils; /** @@ -48,43 +47,23 @@ class Proxy extends BaseModule */ public static function rawContent(array $parameters = []) { - // Set application instance here - $a = DI::app(); - - /* - * Pictures are stored in one of the following ways: - * - * 1. If a folder "proxy" exists and is writeable, then use this for caching - * 2. If a cache path is defined, use this - * 3. If everything else failed, cache into the database - * - * Question: Do we really need these three methods? - */ - if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && isset($_SERVER['HTTP_IF_NONE_MATCH'])) { - header('HTTP/1.1 304 Not Modified'); - header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT'); - header('Etag: ' . $_SERVER['HTTP_IF_NONE_MATCH']); - header('Expires: ' . gmdate('D, d M Y H:i:s', time() + (31536000)) . ' GMT'); - header('Cache-Control: max-age=31536000'); - - if (function_exists('header_remove')) { - header_remove('Last-Modified'); - header_remove('Expires'); - header_remove('Cache-Control'); + if (isset($_SERVER["HTTP_IF_MODIFIED_SINCE"])) { + header("HTTP/1.1 304 Not Modified"); + header("Last-Modified: " . gmdate("D, d M Y H:i:s", time()) . " GMT"); + if (!empty($_SERVER["HTTP_IF_NONE_MATCH"])) { + header("Etag: " . $_SERVER["HTTP_IF_NONE_MATCH"]); } - - /// @TODO Stop here? - exit(); + header("Expires: " . gmdate("D, d M Y H:i:s", time() + (31536000)) . " GMT"); + header("Cache-Control: max-age=31536000"); + if (function_exists("header_remove")) { + header_remove("Last-Modified"); + header_remove("Expires"); + header_remove("Cache-Control"); + } + exit; } - if (function_exists('header_remove')) { - header_remove('Pragma'); - header_remove('pragma'); - } - - $direct_cache = self::setupDirectCache(); - - $request = self::getRequestInfo(); + $request = self::getRequestInfo($parameters); if (empty($request['url'])) { throw new \Friendica\Network\HTTPException\BadRequestException(); @@ -95,35 +74,21 @@ class Proxy extends BaseModule System::externalRedirect($request['url']); } - // Webserver already tried direct cache... - - // Try to use filecache; - $cachefile = self::responseFromCache($request); - - // Try to use photo from db - self::responseFromDB($request); - - // - // If script is here, the requested url has never cached before. - // Let's fetch it, scale it if required, then save it in cache. - // - // It shouldn't happen but it does - spaces in URL $request['url'] = str_replace(' ', '+', $request['url']); + + // Fetch the content with the local user $fetchResult = HTTPSignature::fetchRaw($request['url'], local_user(), ['timeout' => 10]); $img_str = $fetchResult->getBody(); - // If there is an error then return a blank image + // If there is an error then return an error if ((substr($fetchResult->getReturnCode(), 0, 1) == '4') || empty($img_str)) { Logger::info('Error fetching image', ['image' => $request['url'], 'return' => $fetchResult->getReturnCode(), 'empty' => empty($img_str)]); self::responseError(); // stop. } - $tempfile = tempnam(get_temppath(), 'cache'); - file_put_contents($tempfile, $img_str); - $mime = mime_content_type($tempfile); - unlink($tempfile); + $mime = Images::getMimeTypeByData($img_str); $image = new Image($img_str, $mime); if (!$image->isValid()) { @@ -132,80 +97,33 @@ class Proxy extends BaseModule // stop. } - $basepath = $a->getBasePath(); - $filepermission = DI::config()->get('system', 'proxy_file_chmod'); - - // Store original image - if ($direct_cache) { - // direct cache , store under ./proxy/ - $filename = $basepath . '/proxy/' . ProxyUtils::proxifyUrl($request['url'], true); - file_put_contents($filename, $image->asString()); - if (!empty($filepermission)) { - chmod($filename, $filepermission); - } - } elseif($cachefile !== '') { - // cache file - file_put_contents($cachefile, $image->asString()); - } else { - // database - Photo::store($image, 0, 0, $request['urlhash'], $request['url'], '', 100); - } - - // reduce quality - if it isn't a GIF if ($image->getType() != 'image/gif') { $image->scaleDown($request['size']); } - - // Store scaled image - if ($direct_cache && $request['sizetype'] != '') { - $filename = $basepath . '/proxy/' . ProxyUtils::proxifyUrl($request['url'], true) . $request['sizetype']; - file_put_contents($filename, $image->asString()); - if (!empty($filepermission)) { - chmod($filename, $filepermission); - } - } - self::responseImageHttpCache($image); // stop. } - /** * Build info about requested image to be proxied * * @return array * [ * 'url' => requested url, - * 'urlhash' => sha1 has of the url prefixed with 'pic:', * 'size' => requested image size (int) * 'sizetype' => requested image size (string): ':micro', ':thumb', ':small', ':medium', ':large' * ] * @throws \Exception */ - private static function getRequestInfo() + private static function getRequestInfo(array $parameters) { - $a = DI::app(); $size = ProxyUtils::PIXEL_LARGE; $sizetype = ''; - // Look for filename in the arguments - // @TODO: Replace with parameter from router - if (($a->argc > 1) && !isset($_REQUEST['url'])) { - if (isset($a->argv[3])) { - $url = $a->argv[3]; - } elseif (isset($a->argv[2])) { - $url = $a->argv[2]; - } else { - $url = $a->argv[1]; - } - - /// @TODO: Why? And what about $url in this case? - /// @TODO: Replace with parameter from router - if (isset($a->argv[3]) && ($a->argv[3] == 'thumb')) { - $size = 200; - } + if (!empty($parameters['url']) && empty($_REQUEST['url'])) { + $url = $parameters['url']; // thumb, small, medium and large. if (substr($url, -6) == ':micro') { @@ -238,87 +156,17 @@ class Proxy extends BaseModule $url = str_replace(['.jpg', '.jpeg', '.gif', '.png'], ['','','',''], $url); $url = base64_decode(strtr($url, '-_', '+/'), true); - } else { $url = $_REQUEST['url'] ?? ''; } return [ 'url' => $url, - 'urlhash' => 'pic:' . sha1($url), 'size' => $size, 'sizetype' => $sizetype, ]; } - - /** - * setup ./proxy folder for direct cache - * - * @return bool False if direct cache can't be used. - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - */ - private static function setupDirectCache() - { - $a = DI::app(); - $basepath = $a->getBasePath(); - - // If the cache path isn't there, try to create it - if (!is_dir($basepath . '/proxy') && is_writable($basepath)) { - mkdir($basepath . '/proxy'); - } - - // Checking if caching into a folder in the webroot is activated and working - $direct_cache = (is_dir($basepath . '/proxy') && is_writable($basepath . '/proxy')); - // we don't use direct cache if image url is passed in args and not in querystring - $direct_cache = $direct_cache && ($a->argc > 1) && !isset($_REQUEST['url']); - - return $direct_cache; - } - - - /** - * Try to reply with image in cachefile - * - * @param array $request Array from getRequestInfo - * - * @return string Cache file name, empty string if cache is not enabled. - * - * If cachefile exists, script ends here and this function will never returns - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - private static function responseFromCache(&$request) - { - $cachefile = get_cachefile(hash('md5', $request['url'])); - if ($cachefile != '' && file_exists($cachefile)) { - $img = new Image(file_get_contents($cachefile), mime_content_type($cachefile)); - self::responseImageHttpCache($img); - // stop. - } - return $cachefile; - } - - /** - * Try to reply with image in database - * - * @param array $request Array from getRequestInfo - * - * If the image exists in database, then script ends here and this function will never returns - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - private static function responseFromDB(&$request) - { - $photo = Photo::getPhoto($request['urlhash']); - - if ($photo !== false) { - $img = Photo::getImageForPhoto($photo); - self::responseImageHttpCache($img); - // stop. - } - } - /** * In case of an error just stop. We don't return content to avoid caching problems * diff --git a/src/Protocol/OStatus.php b/src/Protocol/OStatus.php index 919f327b89..1019d34cc7 100644 --- a/src/Protocol/OStatus.php +++ b/src/Protocol/OStatus.php @@ -1207,7 +1207,7 @@ class OStatus } // Is it a remote picture? Then make a smaller preview here - $preview = ProxyUtils::proxifyUrl($preview, false, ProxyUtils::SIZE_SMALL); + $preview = ProxyUtils::proxifyUrl($preview, ProxyUtils::SIZE_SMALL); // Is it a local picture? Then make it smaller here $preview = str_replace(["-0.jpg", "-0.png"], ["-2.jpg", "-2.png"], $preview); diff --git a/src/Util/Proxy.php b/src/Util/Proxy.php index 437b52e1e6..839442797e 100644 --- a/src/Util/Proxy.php +++ b/src/Util/Proxy.php @@ -80,13 +80,12 @@ class Proxy * system.proxy_disabled is set to false. * * @param string $url The URL to proxyfy - * @param bool $writemode Returns a local path the remote URL should be saved to * @param string $size One of the ProxyUtils::SIZE_* constants * * @return string The proxyfied URL or relative path * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function proxifyUrl($url, $writemode = false, $size = '') + public static function proxifyUrl($url, $size = '') { // Get application instance $a = DI::app(); @@ -114,17 +113,9 @@ class Proxy // Image URL may have encoded ampersands for display which aren't desirable for proxy $url = html_entity_decode($url, ENT_NOQUOTES, 'utf-8'); - // Creating a sub directory to reduce the amount of files in the cache directory - $basepath = $a->getBasePath() . '/proxy'; - $shortpath = hash('md5', $url); $longpath = substr($shortpath, 0, 2); - if (is_dir($basepath) && $writemode && !is_dir($basepath . '/' . $longpath)) { - mkdir($basepath . '/' . $longpath); - chmod($basepath . '/' . $longpath, 0777); - } - $longpath .= '/' . strtr(base64_encode($url), '+/', '-_'); // Extract the URL extension @@ -142,13 +133,8 @@ class Proxy } // Too long files aren't supported by Apache - // Writemode in combination with long files shouldn't be possible - if ((strlen($proxypath) > 250) && $writemode) { - return $shortpath; - } elseif (strlen($proxypath) > 250) { + if (strlen($proxypath) > 250) { return DI::baseUrl() . '/proxy/' . $shortpath . '?url=' . urlencode($url); - } elseif ($writemode) { - return $longpath; } else { return $proxypath . $size; }