diff options
author | Alexandre Alapetite <alexandre@alapetite.fr> | 2025-04-26 14:19:54 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-04-26 14:19:54 +0200 |
commit | 3776e1e48f33e80eb4b674bb64b419caf3b5a4e2 (patch) | |
tree | f02c1660e7586e1c1439d28b151b743acf9df3f0 | |
parent | d1f9b6c23240c1528065c5590b25620bf6206e4c (diff) | |
download | freshrss-3776e1e48f33e80eb4b674bb64b419caf3b5a4e2.tar.gz freshrss-3776e1e48f33e80eb4b674bb64b419caf3b5a4e2.zip |
Improve favicon hash (#7505)
* Favicon hash proxy
Content provided through a proxy may be completely different, so the feed hash must account for that
* Fix typing
* Hash of Web site in priority for favicons
* Continue
* Revert some minor changes
-rw-r--r-- | app/Models/Feed.php | 56 |
1 files changed, 44 insertions, 12 deletions
diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 072948e1b..fc17c875f 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -63,6 +63,7 @@ class FreshRSS_Feed extends Minz_Model { private int $ttl = self::TTL_DEFAULT; private bool $mute = false; private string $hash = ''; + private string $hashFavicon = ''; private string $lockPath = ''; private string $hubUrl = ''; private string $selfUrl = ''; @@ -91,11 +92,31 @@ class FreshRSS_Feed extends Minz_Model { public function hash(): string { if ($this->hash == '') { $salt = FreshRSS_Context::systemConf()->salt; - $this->hash = hash('crc32b', $salt . $this->url); + $params = $this->url; + $curl_params = $this->attributeArray('curl_params'); + if (is_array($curl_params)) { + // Content provided through a proxy may be completely different + $params .= is_string($curl_params[CURLOPT_PROXY] ?? null) ? $curl_params[CURLOPT_PROXY] : ''; + } + $this->hash = sha1($salt . $params); } return $this->hash; } + public function hashFavicon(): string { + if ($this->hashFavicon == '') { + $salt = FreshRSS_Context::systemConf()->salt; + $params = $this->website(fallback: true); + $curl_params = $this->attributeArray('curl_params'); + if (is_array($curl_params)) { + // Content provided through a proxy may be completely different + $params .= is_string($curl_params[CURLOPT_PROXY] ?? null) ? $curl_params[CURLOPT_PROXY] : ''; + } + $this->hashFavicon = hash('crc32b', $salt . $params); + } + return $this->hashFavicon; + } + public function url(bool $includeCredentials = true): string { return $includeCredentials ? $this->url : \SimplePie\Misc::url_remove_credentials($this->url); } @@ -133,10 +154,19 @@ class FreshRSS_Feed extends Minz_Model { public function name(bool $raw = false): string { return $raw || $this->name != '' ? $this->name : (preg_replace('%^https?://(www[.])?%i', '', $this->url) ?? ''); } - /** @return string HTML-encoded URL of the Web site of the feed */ - public function website(): string { - return $this->website; + + /** + * @param bool $fallback true to return the URL of the feed if the Web site is blank + * @return string HTML-encoded URL of the Web site of the feed + */ + public function website(bool $fallback = false): string { + $url = $this->website; + if ($fallback && !preg_match('%^https?://.%i', $url)) { + $url = $this->url; + } + return $url; } + public function description(): string { return $this->description; } @@ -227,16 +257,13 @@ class FreshRSS_Feed extends Minz_Model { public function faviconPrepare(bool $force = false): void { require_once(LIB_PATH . '/favicons.php'); - $url = $this->website; - if ($url == '') { - $url = $this->url; - } - $txt = FAVICONS_DIR . $this->hash() . '.txt'; + $url = $this->website(fallback: true); + $txt = FAVICONS_DIR . $this->hashFavicon() . '.txt'; if (@file_get_contents($txt) !== $url) { file_put_contents($txt, $url); } if (FreshRSS_Context::$isCli || $force) { - $ico = FAVICONS_DIR . $this->hash() . '.ico'; + $ico = FAVICONS_DIR . $this->hashFavicon() . '.ico'; $ico_mtime = @filemtime($ico); $txt_mtime = @filemtime($txt); if ($txt_mtime != false && @@ -251,12 +278,15 @@ class FreshRSS_Feed extends Minz_Model { } public static function faviconDelete(string $hash): void { + if (!ctype_xdigit($hash)) { + return; + } $path = DATA_PATH . '/favicons/' . $hash; @unlink($path . '.ico'); @unlink($path . '.txt'); } public function favicon(): string { - return Minz_Url::display('/f.php?' . $this->hash()); + return Minz_Url::display('/f.php?' . $this->hashFavicon()); } public function _id(int $value): void { @@ -268,6 +298,7 @@ class FreshRSS_Feed extends Minz_Model { */ public function _url(string $value, bool $validate = true): void { $this->hash = ''; + $this->hashFavicon = ''; $url = $value; if ($validate) { $url = checkUrl($url); @@ -297,6 +328,7 @@ class FreshRSS_Feed extends Minz_Model { $this->name = $value == '' ? '' : trim($value); } public function _website(string $value, bool $validate = true): void { + $this->hashFavicon = ''; if ($validate) { $value = checkUrl($value); } @@ -1037,7 +1069,7 @@ class FreshRSS_Feed extends Minz_Model { } private function faviconRebuild(): void { - FreshRSS_Feed::faviconDelete($this->hash()); + FreshRSS_Feed::faviconDelete($this->hashFavicon()); $this->faviconPrepare(true); } |