diff --git a/Web/Models/Entities/Note.php b/Web/Models/Entities/Note.php index 37d9ac29..932c1fc9 100644 --- a/Web/Models/Entities/Note.php +++ b/Web/Models/Entities/Note.php @@ -2,12 +2,48 @@ namespace openvk\Web\Models\Entities; use HTMLPurifier_Config; use HTMLPurifier; +use HTMLPurifier_Filter; + +class SecurityFilter extends HTMLPurifier_Filter +{ + public function preFilter($html, $config, $context) + { + $html = preg_replace_callback( + '/]*src\s*=\s*["\']([^"\']*)["\'][^>]*>/i', + function ($matches) { + $originalSrc = $matches[1]; + $src = $originalSrc; + if (!str_contains($src, "/image.php?url=")) { + $src = '/image.php?url=' . base64_encode($originalSrc); + } else { + if (!OPENVK_ROOT_CONF["openvk"]["preferences"]["imagesProxy"]["replaceInNotes"]) { + $src = preg_replace_callback('/(.*)\/image\.php\?url=(.*)/i', function ($matches) { + return base64_decode($matches[2]); + }, $src); + } + } + return str_replace($originalSrc, $src, $matches[0]); + }, + $html + ); + + return preg_replace_callback( + '/]*href\s*=\s*["\']([^"\']*)["\'][^>]*>/i', + function ($matches) { + $originalHref = $matches[1]; + $encodedHref = '/away.php?to=' . urlencode($originalHref); + return str_replace($originalHref, $encodedHref, $matches[0]); + }, + $html + ); + } +} class Note extends Postable { protected $tableName = "notes"; - protected function renderHTML(): string + protected function renderHTML(?string $content = NULL): string { $config = HTMLPurifier_Config::createDefault(); $config->set("Attr.AllowedClasses", []); @@ -74,15 +110,18 @@ class Note extends Postable $config->set("Attr.AllowedClasses", [ "underline", ]); - - $source = NULL; - if(is_null($this->getRecord())) { - if(isset($this->changes["source"])) - $source = $this->changes["source"]; - else - throw new \LogicException("Can't render note without content set."); - } else { - $source = $this->getRecord()->source; + $config->set('Filter.Custom', [new SecurityFilter()]); + + $source = $content; + if (!$source) { + if (is_null($this->getRecord())) { + if (isset($this->changes["source"])) + $source = $this->changes["source"]; + else + throw new \LogicException("Can't render note without content set."); + } else { + $source = $this->getRecord()->source; + } } $purifier = new HTMLPurifier($config); @@ -110,8 +149,8 @@ class Note extends Postable $this->setCached_Content($cached); $this->save(); } - - return $cached; + + return $this->renderHTML($cached); } function getSource(): string diff --git a/Web/Presenters/ImagesProxyPresenter.php b/Web/Presenters/ImagesProxyPresenter.php new file mode 100644 index 00000000..6a86b21f --- /dev/null +++ b/Web/Presenters/ImagesProxyPresenter.php @@ -0,0 +1,63 @@ +image("image/png", strlen($placeholder), $placeholder); + } + + public function renderIndex(): void + { + $this->assertUserLoggedIn(); + + $url = $this->requestParam("url"); + if (OPENVK_ROOT_CONF["openvk"]["preferences"]["imagesProxy"]["settings"]["base64_decode_url"]) { + $url = base64_decode($url); + } + + $url = OPENVK_ROOT_CONF["openvk"]["preferences"]["imagesProxy"]["settings"]["url_prefix"] . $url; + if (!$url || !filter_var($url, FILTER_VALIDATE_URL)) { + $this->placeholder(); + } + + $ch = curl_init($url); + curl_setopt_array($ch, [ + CURLOPT_HEADER => 0, + CURLOPT_URL => $url, + CURLOPT_USERAGENT => OPENVK_ROOT_CONF["openvk"]["appearance"]["name"] . ' Images Proxy/1.0', + CURLOPT_REFERER => "https://$_SERVER[SERVER_NAME]/", + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_BINARYTRANSFER => 1, + ]); + + $raw = curl_exec($ch); + $contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); + $contentSize = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD); + + curl_close($ch); + + if ($raw && str_contains($contentType, "image")) { + $this->image($contentType, $contentSize, $raw); + } else { + $this->placeholder(); + } + } +} diff --git a/Web/di.yml b/Web/di.yml index 3363c5de..8cd39417 100644 --- a/Web/di.yml +++ b/Web/di.yml @@ -49,3 +49,4 @@ services: - openvk\Web\Models\Repositories\BannedLinks - openvk\Web\Models\Repositories\ChandlerGroups - openvk\Web\Presenters\MaintenancePresenter + - openvk\Web\Presenters\ImagesProxyPresenter diff --git a/Web/routes.yml b/Web/routes.yml index d1a0e7ae..d69da10f 100644 --- a/Web/routes.yml +++ b/Web/routes.yml @@ -349,6 +349,8 @@ routes: handler: "About->dev" - url: "/tour" handler: "About->tour" + - url: "/image.php" + handler: "ImagesProxy->index" - url: "/{?shortCode}" handler: "UnknownTextRouteStrategy->delegate" placeholders: diff --git a/openvk-example.yml b/openvk-example.yml index e3fd1c3a..8cf75b1d 100644 --- a/openvk-example.yml +++ b/openvk-example.yml @@ -102,6 +102,11 @@ openvk: fartscroll: false testLabel: false defaultMobileTheme: "" + imagesProxy: + replaceInNotes: true + settings: + url_prefix: "" + base64_decode_url: true telemetry: plausible: