5
5
final class ImageProxyExtension extends Minz_Extension {
6
6
// Defaults
7
7
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 ;
10
10
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 ;
13
13
14
+ /**
15
+ * @throws FreshRSS_Context_Exception
16
+ */
14
17
#[\Override]
15
18
public function init (): void {
16
19
if (!FreshRSS_Context::hasSystemConf ()) {
@@ -19,114 +22,122 @@ public function init(): void {
19
22
$ this ->registerHook ('entry_before_display ' , [self ::class, 'setImageProxyHook ' ]);
20
23
// Defaults
21
24
$ 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 ) ;
24
27
$ save = true ;
25
28
}
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 ) ;
28
31
$ save = true ;
29
32
}
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 );
37
35
$ save = true ;
38
36
}
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 ) ;
41
39
$ save = true ;
42
40
}
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 ) ;
45
43
$ save = true ;
46
44
}
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 ) ;
49
47
$ save = true ;
50
48
}
51
49
if ($ save ) {
52
50
FreshRSS_Context::userConf ()->save ();
53
51
}
54
52
}
55
53
54
+ /**
55
+ * @throws FreshRSS_Context_Exception
56
+ */
56
57
#[\Override]
57
58
public function handleConfigureAction (): void {
58
59
$ this ->registerTranslates ();
59
60
60
61
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 ' ) );
67
68
FreshRSS_Context::userConf ()->save ();
68
69
}
69
70
}
70
71
72
+ /**
73
+ * @throws FreshRSS_Context_Exception
74
+ */
71
75
public static function getProxyImageUri (string $ url ): string {
72
76
$ parsed_url = parse_url ($ url );
73
- $ scheme = isset ( $ parsed_url ['scheme ' ]) ? $ parsed_url [ ' scheme ' ] : null ;
77
+ $ scheme = $ parsed_url ['scheme ' ] ?? '' ;
74
78
if ($ scheme === 'http ' ) {
75
- if (!FreshRSS_Context::userConf ()->image_proxy_scheme_http ) {
79
+ if (!FreshRSS_Context::userConf ()->attributeBool ( ' image_proxy_scheme_http ' ) ) {
76
80
return $ url ;
77
81
}
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://
80
84
}
81
85
} elseif ($ scheme === 'https ' ) {
82
- if (!FreshRSS_Context::userConf ()->image_proxy_scheme_https ) {
86
+ if (!FreshRSS_Context::userConf ()->attributeBool ( ' image_proxy_scheme_https ' ) ) {
83
87
return $ url ;
84
88
}
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://
87
91
}
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 ;
92
96
}
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 ;
96
100
}
97
- } else { // do not proxy unschemed ("//path/...") URLs
101
+ } else { // do not proxy unschemed ("//path/...") URLs
98
102
return $ url ;
99
103
}
100
- } else { // unknown/unsupported (non-http) scheme
104
+ } else { // unknown/unsupported (non-http) scheme
101
105
return $ url ;
102
106
}
103
- if (FreshRSS_Context::userConf ()->image_proxy_url_encode ) {
107
+ if (FreshRSS_Context::userConf ()->attributeBool ( ' image_proxy_url_encode ' ) ) {
104
108
$ url = rawurlencode ($ url );
105
109
}
106
- return FreshRSS_Context::userConf ()->image_proxy_url . $ url ;
110
+ return FreshRSS_Context::userConf ()->attributeString ( ' image_proxy_url ' ) . $ url ;
107
111
}
108
112
109
113
/**
110
114
* @param array<string> $matches
115
+ * @throws FreshRSS_Context_Exception
111
116
*/
112
117
public static function getSrcSetUris (array $ matches ): string {
113
118
return str_replace ($ matches [1 ], self ::getProxyImageUri ($ matches [1 ]), $ matches [0 ]);
114
119
}
115
120
121
+ /**
122
+ * @throws FreshRSS_Context_Exception
123
+ */
116
124
public static function swapUris (string $ content ): string {
117
- if (empty ( $ content) ) {
125
+ if ($ content === '' ) {
118
126
return $ content ;
119
127
}
120
128
121
129
$ 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
123
131
$ doc ->loadHTML (mb_convert_encoding ($ content , 'HTML-ENTITIES ' , 'UTF-8 ' ));
124
132
$ imgs = $ doc ->getElementsByTagName ('img ' );
125
133
foreach ($ imgs as $ img ) {
134
+ if (!($ img instanceof DOMElement)) {
135
+ continue ;
136
+ }
126
137
if ($ img ->hasAttribute ('src ' )) {
127
138
$ src = $ img ->getAttribute ('src ' );
128
139
$ newSrc = self ::getProxyImageUri ($ src );
129
- /*
140
+ /*
130
141
Due to the URL change, FreshRSS is not aware of already rendered enclosures.
131
142
Adding data-xextension-imageproxy-original-src / srcset ensures that original URLs are present in the content for the renderer check FreshRSS_Entry->containsLink.
132
143
*/
@@ -146,12 +157,18 @@ public static function swapUris(string $content): string {
146
157
$ body = $ doc ->getElementsByTagName ('body ' )->item (0 );
147
158
148
159
$ output = $ doc ->saveHTML ($ body );
160
+ if ($ output === false ) {
161
+ return '' ;
162
+ }
149
163
150
- $ output = preg_replace ('/^<body>|<\/body>$/ ' , '' , $ output );
164
+ $ output = preg_replace ('/^<body>|<\/body>$/ ' , '' , $ output ) ?? '' ;
151
165
152
166
return $ output ;
153
167
}
154
168
169
+ /**
170
+ * @throws FreshRSS_Context_Exception
171
+ */
155
172
public static function setImageProxyHook (FreshRSS_Entry $ entry ): FreshRSS_Entry {
156
173
$ entry ->_content (
157
174
self ::swapUris ($ entry ->content ())
0 commit comments