Skip to content

Commit 4dd0450

Browse files
authored
Update ImageProxy code (#270)
* Update ImageProxy code fix #202 Note: breaking changes. Users will have to save their configuration again * Readme changelog * Fixes
1 parent 6b6ea64 commit 4dd0450

File tree

7 files changed

+85
-61
lines changed

7 files changed

+85
-61
lines changed

phpstan.dist.neon

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ parameters:
1212
excludePaths:
1313
analyse:
1414
- ../FreshRSS
15-
- xExtension-ImageProxy/configure.phtml # TODO pass
16-
- xExtension-ImageProxy/extension.php # TODO pass
1715
analyseAndScan:
1816
- .git/*?
1917
- node_modules/*?
@@ -39,5 +37,7 @@ parameters:
3937
implicitThrows: false
4038
checkedExceptionClasses:
4139
- 'Minz_Exception'
40+
ignoreErrors:
41+
- '#Only booleans are allowed in (a negated boolean|a ternary operator condition|an elseif condition|an if condition|&&|\|\|), (bool|false|int(<[0-9, max]+>)?|true|null|\|)+ given.*#'
4242
includes:
4343
- vendor/phpstan/phpstan-strict-rules/rules.neon

xExtension-ImageProxy/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ To use it, upload this entire directory to the FreshRSS `./extensions` directory
66

77
## Changelog
88

9+
* 1.0 Breaking changes due to significant code upgrade: settings must be saved again
910
* 0.7.3 Turkish language support added
1011

1112
## Configuration settings

xExtension-ImageProxy/configure.phtml

+12-6
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,30 @@
77
<div class="form-group">
88
<label class="group-name" for="image_proxy_url"><?= _t('ext.imageproxy.proxy_url') ?></label>
99
<div class="group-controls">
10-
<input type="url" name="image_proxy_url" id="image_proxy_url" value="<?= FreshRSS_Context::userConf()->image_proxy_url ?>">
10+
<input type="url" name="image_proxy_url" id="image_proxy_url"
11+
value="<?= htmlspecialchars(FreshRSS_Context::userConf()->attributeString('image_proxy_url') ?? '', ENT_COMPAT, 'UTF-8') ?>">
1112
</div>
1213
</div>
1314
<div class="form-group">
1415
<label class="group-name" for="image_proxy_scheme_http"><?= _t('ext.imageproxy.scheme_http') ?></label>
1516
<div class="group-controls">
16-
<input type="checkbox" name="image_proxy_scheme_http" id="image_proxy_scheme_http" value="1" <?= FreshRSS_Context::userConf()->image_proxy_scheme_http ? 'checked' : '' ?>>
17+
<input type="checkbox" name="image_proxy_scheme_http" id="image_proxy_scheme_http" value="1"
18+
<?= FreshRSS_Context::userConf()->attributeBool('image_proxy_scheme_http') ? 'checked' : '' ?>>
1719
</div>
1820
</div>
1921
<div class="form-group">
2022
<label class="group-name" for="image_proxy_scheme_https"><?= _t('ext.imageproxy.scheme_https'); ?></label>
2123
<div class="group-controls">
22-
<input type="checkbox" name="image_proxy_scheme_https" id="image_proxy_scheme_https" value="1" <?= FreshRSS_Context::userConf()->image_proxy_scheme_https ? 'checked' : '' ?>>
24+
<input type="checkbox" name="image_proxy_scheme_https" id="image_proxy_scheme_https" value="1"
25+
<?= FreshRSS_Context::userConf()->attributeBool('image_proxy_scheme_https') ? 'checked' : '' ?>>
2326
</div>
2427
</div>
2528
<div class="form-group">
2629
<label class="group-name" for="image_proxy_scheme_default"><?= _t('ext.imageproxy.scheme_default'); ?></label>
2730
<div class="group-controls">
2831
<select name="image_proxy_scheme_default" id="image_proxy_scheme_default">
29-
<option value="<?= htmlentities(FreshRSS_Context::userConf()->image_proxy_scheme_default ?? '') ?>" selected="selected"><?= htmlentities(FreshRSS_Context::userConf()->image_proxy_scheme_default ?? '') ?></option>
32+
<option value="<?= htmlspecialchars(FreshRSS_Context::userConf()->attributeString('image_proxy_scheme_default') ?? '', ENT_COMPAT, 'UTF-8') ?>" selected="selected"><?=
33+
htmlspecialchars(FreshRSS_Context::userConf()->attributeString('image_proxy_scheme_default') ?? '', ENT_COMPAT, 'UTF-8') ?></option>
3034
<option value="-">-</option>
3135
<option value="auto">auto</option>
3236
<option value="http">http</option>
@@ -37,13 +41,15 @@
3741
<div class="form-group">
3842
<label class="group-name" for="image_proxy_scheme_include"><?= _t('ext.imageproxy.scheme_include'); ?></label>
3943
<div class="group-controls">
40-
<input type="checkbox" name="image_proxy_scheme_include" id="image_proxy_scheme_include" value="1" <?= FreshRSS_Context::userConf()->image_proxy_scheme_include ? 'checked' : '' ?>>
44+
<input type="checkbox" name="image_proxy_scheme_include" id="image_proxy_scheme_include" value="1"
45+
<?= FreshRSS_Context::userConf()->attributeBool('image_proxy_scheme_include') ? 'checked' : '' ?>>
4146
</div>
4247
</div>
4348
<div class="form-group">
4449
<label class="group-name" for="image_proxy_url_encode"><?= _t('ext.imageproxy.url_encode'); ?></label>
4550
<div class="group-controls">
46-
<input type="checkbox" name="image_proxy_url_encode" id="image_proxy_url_encode" value="1" <?= FreshRSS_Context::userConf()->image_proxy_url_encode ? 'checked' : '' ?>>
51+
<input type="checkbox" name="image_proxy_url_encode" id="image_proxy_url_encode" value="1"
52+
<?= FreshRSS_Context::userConf()->attributeBool('image_proxy_url_encode') ? 'checked' : '' ?>>
4753
</div>
4854
</div>
4955

xExtension-ImageProxy/extension.php

+66-49
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
final class ImageProxyExtension extends Minz_Extension {
66
// Defaults
77
private const PROXY_URL = 'https://wsrv.nl/?url=';
8-
private const SCHEME_HTTP = '1';
9-
private const SCHEME_HTTPS = '';
8+
private const SCHEME_HTTP = true;
9+
private const SCHEME_HTTPS = false;
1010
private const SCHEME_DEFAULT = 'auto';
11-
private const SCHEME_INCLUDE = '';
12-
private const URL_ENCODE = '1';
11+
private const SCHEME_INCLUDE = false;
12+
private const URL_ENCODE = true;
1313

14+
/**
15+
* @throws FreshRSS_Context_Exception
16+
*/
1417
#[\Override]
1518
public function init(): void {
1619
if (!FreshRSS_Context::hasSystemConf()) {
@@ -19,114 +22,122 @@ public function init(): void {
1922
$this->registerHook('entry_before_display', [self::class, 'setImageProxyHook']);
2023
// Defaults
2124
$save = false;
22-
if (is_null(FreshRSS_Context::userConf()->image_proxy_url)) {
23-
FreshRSS_Context::userConf()->image_proxy_url = self::PROXY_URL;
25+
if (FreshRSS_Context::userConf()->attributeString('image_proxy_url') == null) {
26+
FreshRSS_Context::userConf()->_attribute('image_proxy_url', self::PROXY_URL);
2427
$save = true;
2528
}
26-
if (is_null(FreshRSS_Context::userConf()->image_proxy_scheme_http)) {
27-
FreshRSS_Context::userConf()->image_proxy_scheme_http = self::SCHEME_HTTP;
29+
if (FreshRSS_Context::userConf()->attributeBool('image_proxy_scheme_http') === null) {
30+
FreshRSS_Context::userConf()->_attribute('image_proxy_scheme_http', self::SCHEME_HTTP);
2831
$save = true;
2932
}
30-
if (is_null(FreshRSS_Context::userConf()->image_proxy_scheme_https)) {
31-
FreshRSS_Context::userConf()->image_proxy_scheme_https = self::SCHEME_HTTPS;
32-
// Legacy
33-
if (!is_null(FreshRSS_Context::userConf()->image_proxy_force)) {
34-
FreshRSS_Context::userConf()->image_proxy_scheme_https = FreshRSS_Context::userConf()->image_proxy_force;
35-
FreshRSS_Context::userConf()->image_proxy_force = null; // Minz -> unset
36-
}
33+
if (FreshRSS_Context::userConf()->attributeBool('image_proxy_scheme_https') === null) {
34+
FreshRSS_Context::userConf()->_attribute('image_proxy_scheme_https', self::SCHEME_HTTPS);
3735
$save = true;
3836
}
39-
if (is_null(FreshRSS_Context::userConf()->image_proxy_scheme_default)) {
40-
FreshRSS_Context::userConf()->image_proxy_scheme_default = self::SCHEME_DEFAULT;
37+
if (FreshRSS_Context::userConf()->attributeString('image_proxy_scheme_default') === null) {
38+
FreshRSS_Context::userConf()->_attribute('image_proxy_scheme_default', self::SCHEME_DEFAULT);
4139
$save = true;
4240
}
43-
if (is_null(FreshRSS_Context::userConf()->image_proxy_scheme_include)) {
44-
FreshRSS_Context::userConf()->image_proxy_scheme_include = self::SCHEME_INCLUDE;
41+
if (FreshRSS_Context::userConf()->attributeBool('image_proxy_scheme_include') === null) {
42+
FreshRSS_Context::userConf()->_attribute('image_proxy_scheme_include', self::SCHEME_INCLUDE);
4543
$save = true;
4644
}
47-
if (is_null(FreshRSS_Context::userConf()->image_proxy_url_encode)) {
48-
FreshRSS_Context::userConf()->image_proxy_url_encode = self::URL_ENCODE;
45+
if (FreshRSS_Context::userConf()->attributeBool('image_proxy_url_encode') === null) {
46+
FreshRSS_Context::userConf()->_attribute('image_proxy_url_encode', self::URL_ENCODE);
4947
$save = true;
5048
}
5149
if ($save) {
5250
FreshRSS_Context::userConf()->save();
5351
}
5452
}
5553

54+
/**
55+
* @throws FreshRSS_Context_Exception
56+
*/
5657
#[\Override]
5758
public function handleConfigureAction(): void {
5859
$this->registerTranslates();
5960

6061
if (Minz_Request::isPost()) {
61-
FreshRSS_Context::userConf()->image_proxy_url = Minz_Request::paramString('image_proxy_url', true) ?: self::PROXY_URL;
62-
FreshRSS_Context::userConf()->image_proxy_scheme_http = Minz_Request::paramString('image_proxy_scheme_http');
63-
FreshRSS_Context::userConf()->image_proxy_scheme_https = Minz_Request::paramString('image_proxy_scheme_https');
64-
FreshRSS_Context::userConf()->image_proxy_scheme_default = Minz_Request::paramString('image_proxy_scheme_default') ?: self::SCHEME_DEFAULT;
65-
FreshRSS_Context::userConf()->image_proxy_scheme_include = Minz_Request::paramString('image_proxy_scheme_include');
66-
FreshRSS_Context::userConf()->image_proxy_url_encode = Minz_Request::paramString('image_proxy_url_encode');
62+
FreshRSS_Context::userConf()->_attribute('image_proxy_url', Minz_Request::paramString('image_proxy_url', plaintext: true) ?: self::PROXY_URL);
63+
FreshRSS_Context::userConf()->_attribute('image_proxy_scheme_http', Minz_Request::paramBoolean('image_proxy_scheme_http'));
64+
FreshRSS_Context::userConf()->_attribute('image_proxy_scheme_https', Minz_Request::paramBoolean('image_proxy_scheme_https'));
65+
FreshRSS_Context::userConf()->_attribute('image_proxy_scheme_default', Minz_Request::paramString('image_proxy_scheme_default', plaintext: true) ?: self::SCHEME_DEFAULT);
66+
FreshRSS_Context::userConf()->_attribute('image_proxy_scheme_include', Minz_Request::paramBoolean('image_proxy_scheme_include'));
67+
FreshRSS_Context::userConf()->_attribute('image_proxy_url_encode', Minz_Request::paramBoolean('image_proxy_url_encode'));
6768
FreshRSS_Context::userConf()->save();
6869
}
6970
}
7071

72+
/**
73+
* @throws FreshRSS_Context_Exception
74+
*/
7175
public static function getProxyImageUri(string $url): string {
7276
$parsed_url = parse_url($url);
73-
$scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] : null;
77+
$scheme = $parsed_url['scheme'] ?? '';
7478
if ($scheme === 'http') {
75-
if (!FreshRSS_Context::userConf()->image_proxy_scheme_http) {
79+
if (!FreshRSS_Context::userConf()->attributeBool('image_proxy_scheme_http')) {
7680
return $url;
7781
}
78-
if (!FreshRSS_Context::userConf()->image_proxy_scheme_include) {
79-
$url = substr($url, 7); // http://
82+
if (!FreshRSS_Context::userConf()->attributeBool('image_proxy_scheme_include')) {
83+
$url = substr($url, 7); // http://
8084
}
8185
} elseif ($scheme === 'https') {
82-
if (!FreshRSS_Context::userConf()->image_proxy_scheme_https) {
86+
if (!FreshRSS_Context::userConf()->attributeBool('image_proxy_scheme_https')) {
8387
return $url;
8488
}
85-
if (!FreshRSS_Context::userConf()->image_proxy_scheme_include) {
86-
$url = substr($url, 8); // https://
89+
if (!FreshRSS_Context::userConf()->attributeBool('image_proxy_scheme_include')) {
90+
$url = substr($url, 8); // https://
8791
}
88-
} elseif (empty($scheme)) {
89-
if (FreshRSS_Context::userConf()->image_proxy_scheme_default === 'auto') {
90-
if (FreshRSS_Context::userConf()->image_proxy_scheme_include) {
91-
$url = ((!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off') ? 'https:' : 'http:') . $url;
92+
} elseif ($scheme === '') {
93+
if (FreshRSS_Context::userConf()->attributeString('image_proxy_scheme_default') === 'auto') {
94+
if (FreshRSS_Context::userConf()->attributeBool('image_proxy_scheme_include')) {
95+
$url = ((is_string($_SERVER['HTTPS'] ?? null) && strtolower($_SERVER['HTTPS']) !== 'off') ? 'https:' : 'http:') . $url;
9296
}
93-
} elseif (substr(FreshRSS_Context::userConf()->image_proxy_scheme_default, 0, 4) === 'http') {
94-
if (FreshRSS_Context::userConf()->image_proxy_scheme_include) {
95-
$url = FreshRSS_Context::userConf()->image_proxy_scheme_default . ':' . $url;
97+
} elseif (str_starts_with(FreshRSS_Context::userConf()->attributeString('image_proxy_scheme_default') ?? '', 'http')) {
98+
if (FreshRSS_Context::userConf()->attributeBool('image_proxy_scheme_include')) {
99+
$url = FreshRSS_Context::userConf()->attributeString('image_proxy_scheme_default') . ':' . $url;
96100
}
97-
} else { // do not proxy unschemed ("//path/...") URLs
101+
} else { // do not proxy unschemed ("//path/...") URLs
98102
return $url;
99103
}
100-
} else { // unknown/unsupported (non-http) scheme
104+
} else { // unknown/unsupported (non-http) scheme
101105
return $url;
102106
}
103-
if (FreshRSS_Context::userConf()->image_proxy_url_encode) {
107+
if (FreshRSS_Context::userConf()->attributeBool('image_proxy_url_encode')) {
104108
$url = rawurlencode($url);
105109
}
106-
return FreshRSS_Context::userConf()->image_proxy_url . $url;
110+
return FreshRSS_Context::userConf()->attributeString('image_proxy_url') . $url;
107111
}
108112

109113
/**
110114
* @param array<string> $matches
115+
* @throws FreshRSS_Context_Exception
111116
*/
112117
public static function getSrcSetUris(array $matches): string {
113118
return str_replace($matches[1], self::getProxyImageUri($matches[1]), $matches[0]);
114119
}
115120

121+
/**
122+
* @throws FreshRSS_Context_Exception
123+
*/
116124
public static function swapUris(string $content): string {
117-
if (empty($content)) {
125+
if ($content === '') {
118126
return $content;
119127
}
120128

121129
$doc = new DOMDocument();
122-
libxml_use_internal_errors(true); // prevent tag soup errors from showing
130+
libxml_use_internal_errors(true); // prevent tag soup errors from showing
123131
$doc->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'));
124132
$imgs = $doc->getElementsByTagName('img');
125133
foreach ($imgs as $img) {
134+
if (!($img instanceof DOMElement)) {
135+
continue;
136+
}
126137
if ($img->hasAttribute('src')) {
127138
$src = $img->getAttribute('src');
128139
$newSrc = self::getProxyImageUri($src);
129-
/*
140+
/*
130141
Due to the URL change, FreshRSS is not aware of already rendered enclosures.
131142
Adding data-xextension-imageproxy-original-src / srcset ensures that original URLs are present in the content for the renderer check FreshRSS_Entry->containsLink.
132143
*/
@@ -146,12 +157,18 @@ public static function swapUris(string $content): string {
146157
$body = $doc->getElementsByTagName('body')->item(0);
147158

148159
$output = $doc->saveHTML($body);
160+
if ($output === false) {
161+
return '';
162+
}
149163

150-
$output = preg_replace('/^<body>|<\/body>$/', '', $output);
164+
$output = preg_replace('/^<body>|<\/body>$/', '', $output) ?? '';
151165

152166
return $output;
153167
}
154168

169+
/**
170+
* @throws FreshRSS_Context_Exception
171+
*/
155172
public static function setImageProxyHook(FreshRSS_Entry $entry): FreshRSS_Entry {
156173
$entry->_content(
157174
self::swapUris($entry->content())

xExtension-ImageProxy/i18n/tr/ext.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
'scheme_default' => 'Belirtilmemiş Vekil Sunucusu',
99
'scheme_include' => 'Bağlantıya http*:// ekle',
1010
'url_encode' => 'Bağlantıyı kodla',
11-
),
11+
),
1212
);

xExtension-ImageProxy/metadata.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "Image Proxy",
33
"author": "Frans de Jonge",
44
"description": "No insecure content warnings or disappearing images.",
5-
"version": "0.7.3",
5+
"version": "1.0",
66
"entrypoint": "ImageProxy",
77
"type": "user"
88
}

xExtension-YouTube/extension.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -218,13 +218,13 @@ public function getHtml(FreshRSS_Entry $entry, string $url): string
218218
// but we keep it in the content anyway, so RSS clients can extract it to display a preview where it wants (in article listing,
219219
// by example, like with Reeder).
220220
if ($thumbnails->length > 0 && $thumbnails[0] instanceof DOMNode) {
221-
$content .= '<p hidden><img class="enclosure-thumbnail" src="' . $thumbnails[0]->nodeValue . '" alt=""/></p>';
221+
$content .= '<p hidden><img class="enclosure-thumbnail" src="' . $thumbnails[0]->nodeValue . '" alt="" /></p>';
222222
}
223223

224224
$content .= $iframe;
225225

226226
if ($descriptions->length > 0 && $descriptions[0] instanceof DOMNode) {
227-
$content .= '<p class="enclosure-description">' . nl2br(htmlentities($descriptions[0]->nodeValue ?? '')) . '</p>';
227+
$content .= '<p class="enclosure-description">' . nl2br(htmlspecialchars($descriptions[0]->nodeValue ?? '', ENT_COMPAT, 'UTF-8'), use_xhtml: true) . '</p>';
228228
}
229229

230230
$content .= "</div>\n";

0 commit comments

Comments
 (0)