aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorAlexandre Alapetite <alexandre@alapetite.fr>2025-04-26 14:19:54 +0200
committerGitHub <noreply@github.com>2025-04-26 14:19:54 +0200
commit3776e1e48f33e80eb4b674bb64b419caf3b5a4e2 (patch)
treef02c1660e7586e1c1439d28b151b743acf9df3f0
parentd1f9b6c23240c1528065c5590b25620bf6206e4c (diff)
downloadfreshrss-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.php56
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);
}