<optiontype>boolean</optiontype>
<defaultvalue>0</defaultvalue>
</option>
+ <option name="image_proxy_insecure_only">
+ <categoryname>message.general.image</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>0</defaultvalue>
+ </option>
<option name="image_proxy_expiration">
<categoryname>message.general.image</categoryname>
<optiontype>integer</optiontype>
<minvalue>7</minvalue>
<suffix>days</suffix>
</option>
+ <option name="image_proxy_host_whitelist">
+ <categoryname>message.general.image</categoryname>
+ <optiontype>textarea</optiontype>
+ </option>
<!-- /message.general.image -->
<!-- message.censorship -->
define('ENABLE_SHARE_BUTTONS', 1);
define('SHARE_BUTTONS_PROVIDERS', '');
define('MODULE_IMAGE_PROXY', 0);
+define('IMAGE_PROXY_INSECURE_ONLY', 0);
define('IMAGE_PROXY_EXPIRATION', 14);
+define('IMAGE_PROXY_HOST_WHITELIST', '');
define('ENABLE_CENSORSHIP', 0);
define('CENSORED_WORDS', '');
define('REGISTER_ENABLE_PASSWORD_SECURITY_CHECK', 0);
namespace wcf\system\html\output\node;
use wcf\data\smiley\Smiley;
use wcf\data\smiley\SmileyCache;
+use wcf\system\application\ApplicationHandler;
use wcf\system\html\node\AbstractHtmlNodeProcessor;
use wcf\system\request\LinkHandler;
+use wcf\system\request\RouteHandler;
use wcf\system\WCF;
use wcf\util\exception\CryptoException;
use wcf\util\CryptoUtil;
continue;
}
+ if (IMAGE_PROXY_INSECURE_ONLY && $urlComponents['scheme'] === 'https') {
+ // proxy is enabled for insecure connections only
+ continue;
+ }
+
+ if ($this->bypassProxy($urlComponents['host'])) {
+ // check if page was requested over a secure connection
+ // but the link is insecure
+ if (RouteHandler::secureConnection() && $urlComponents['scheme'] === 'http') {
+ // rewrite protocol to `https`
+ $element->setAttribute('src', preg_replace('~^http~', 'https', $src));
+ }
+
+ continue;
+ }
+
$element->setAttribute('data-valid', 'true');
if (!empty($urlComponents['path']) && preg_match('~\.svg~', basename($urlComponents['path']))) {
}
}
+ /**
+ * Validates the domain name against the list of own domains
+ * and whitelisted ones with wildcard support.
+ *
+ * @param string $hostname
+ * @return boolean
+ */
+ protected function bypassProxy($hostname) {
+ static $hosts = null;
+ static $validHosts = [];
+
+ if ($hosts === null) {
+ $whitelist = explode("\n", StringUtil::unifyNewlines(IMAGE_PROXY_HOST_WHITELIST));
+ foreach ($whitelist as $host) {
+ $isWildcard = false;
+ if (mb_strpos($host, '*') !== false) {
+ $host = preg_replace('~^(\*\.)+~', '', $host);
+ if (mb_strpos($host, '*') !== false || $host === '') {
+ // bad host
+ continue;
+ }
+
+ $isWildcard = true;
+ }
+
+ $host = mb_strtolower($host);
+ if (!isset($hosts[$host])) $hosts[$host] = $isWildcard;
+ }
+
+ foreach (ApplicationHandler::getInstance()->getApplications() as $application) {
+ $host = mb_strtolower($application->domainName);
+ if (!isset($hosts[$host])) $hosts[$host] = false;
+ }
+ }
+
+ $hostname = mb_strtolower($hostname);
+ if (isset($hosts[$hostname]) || isset($validHosts[$hostname])) {
+ return true;
+ }
+ else {
+ // check wildcard hosts
+ foreach ($hosts as $host => $isWildcard) {
+ if ($isWildcard && mb_strpos($hostname, $host) !== false) {
+ // the prepended dot will ensure that `example.com` matches only
+ // on domains like `foo.example.com` but not on `bar-example.com`
+ if (StringUtil::endsWith($hostname, '.' . $host)) {
+ $validHosts[$hostname] = $hostname;
+
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
/**
* Returns the link to fetch the image using the image proxy.
*
<item name="wcf.acp.option.module_article"><![CDATA[Artikel]]></item>
<item name="wcf.acp.option.module_image_proxy"><![CDATA[Zwischenspeicherung von externen Bilder aktivieren]]></item>
<item name="wcf.acp.option.image_proxy_expiration"><![CDATA[Speicherzeit]]></item>
+ <item name="wcf.acp.option.image_proxy_insecure_only"><![CDATA[Nur Bilder aus unverschlüsselten Quellen zwischenspeichern]]></item>
+ <item name="wcf.acp.option.image_proxy_host_whitelist"><![CDATA[Ausnahmen von der Zwischenspeicherung]]></item>
+ <item name="wcf.acp.option.image_proxy_host_whitelist.description"><![CDATA[Die aufgeführten Domains werden von der Zwischenspeicherung ausgenommen, die eigene Domain ist implizit enthalten. Der Abgleich erfolgt auf Basis der strikten Übereinstimmung, optional können Subdomains mit einem Platzhalter berücksichtigt werden: <kbd>*.example.com</kbd> umfasst sowohl <kbd>example.com</kbd> als auch Subdomains wie <kbd>foo.example.com</kbd> oder <kbd>www.example.com</kbd>.<br>Bitte nur eine Domain pro Zeile eingeben.]]></item>
<item name="wcf.acp.option.share_buttons_providers"><![CDATA[Anbieter zum Teilen von Inhalten]]></item>
<item name="wcf.acp.option.show_style_changer"><![CDATA[Stil-Auswahl anzeigen]]></item>
<item name="wcf.acp.option.language_use_informal_variant"><![CDATA[Informelle Anrede verwenden]]></item>
<item name="wcf.acp.option.module_article"><![CDATA[Articles]]></item>
<item name="wcf.acp.option.module_image_proxy"><![CDATA[Enable image proxy]]></item>
<item name="wcf.acp.option.image_proxy_expiration"><![CDATA[Storage Time Period]]></item>
+ <item name="wcf.acp.option.image_proxy_insecure_only"><![CDATA[Store images from insecure sources only]]></item>
+ <item name="wcf.acp.option.image_proxy_host_whitelist"><![CDATA[Image proxy whitelist]]></item>
+ <item name="wcf.acp.option.image_proxy_host_whitelist.description"><![CDATA[The listed domains will not be handled by the image proxy, the current domain is implicitly added. Hostnames are exact matches only, a leading wildcard can be used to exclude an entire domain: <kbd>*.example.com</kbd> matches <kbd>example.com</kbd> and subdomains such as <kbd>foo.example.com</kbd> or <kbd>www.example.com</kbd>.<br>Enter one domain per line only.]]></item>
<item name="wcf.acp.option.share_buttons_providers"><![CDATA[Share Button Providers]]></item>
<item name="wcf.acp.option.show_style_changer"><![CDATA[Enable style changer]]></item>
<item name="wcf.acp.option.language_use_informal_variant"><![CDATA[Use informal language variant]]></item>