Commit f79a3299 authored by Benni Mack's avatar Benni Mack
Browse files

[!!!][TASK] Remove config.spamProtectEmailAddresses = ascii

Since modern browsers already decode this "security"
measure, the option config.spamProtectEmailAddresses = ascii
is removed from TYPO3 Core.

Existing installations having the option set to "ascii"
will now behave as this option would be set to "0" (= disabled).

Resolves: #90044
Releases: main
Change-Id: I7fb4266e998ce7f2555f46e6c3ea216745ca54b6
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/72908

Tested-by: Stefan Froemken's avatarStefan Froemken <froemken@gmail.com>
Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: Stefan Froemken's avatarStefan Froemken <froemken@gmail.com>
Reviewed-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
parent 16d97a0d
.. include:: ../../Includes.txt
===============================================================================
Breaking: #90044 - config.spamProtectEmailAddresses with option "ascii" removed
===============================================================================
See :issue:`90044`
Description
===========
The TypoScript setting `config.spamProtectEmailAddresses` set to `ascii` has no
effect anymore as the ASCII-encryption feature has been removed.
The option changed any links to emails like `href="mailto:benni@example.com"`
to point to the ASCII-encoded equivalent. Since all browsers (and most bots/crawlers)
do this automatically and instantly this feature has no spam-protection
relevance anymore.
Impact
======
Setting the option to `ascii` has no effect anymore, which is the same as not
setting the option at all. However, in case the option is set to `ascii` a
deprecation error is thrown.
Affected Installations
======================
TYPO3 installations having this option set in their TypoScript setup.
Migration
=========
In case you still want to keep a email SPAM protection around, it is recommended
to set the option `config.spamProtectEmailAddresses` to a numeric value between
-10 and 10.
Alternatively, there is an extension called `emailobfuscator` available in the
TYPO3 Extension Repository, which also aims to achieve a similar behaviour.
.. index:: Frontend, TypoScript, NotScanned, ext:frontend
......@@ -111,13 +111,6 @@ class DefaultSanitizerBuilderTest extends FunctionalTestCase
'<a href="tel:123456789" role="button">value</a>',
'<a href="tel:123456789" role="button">value</a>',
],
'#057' => [
// config.spamProtectEmailAddresses = ascii
'<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#115;&#111;&#109;&#101;&#46;&#98;&#111;&#100;&#121;&#64;&#116;&#101;&#115;&#116;&#46;&#116;&#121;&#112;&#111;&#51;&#46;&#111;&#114;&#103;">some.body(at)test.typo3(dot)org</a>',
// HTML entity encoding is not really a "protection", `Masterminds/html5-php` per default
// decodes those entities, which is good to have normalized attr values
'<a href="mailto:some.body@test.typo3.org">some.body(at)test.typo3(dot)org</a>',
],
'#090' => [
'<p data-bool><span data-bool><strong data-bool>value</strong></span></p>',
'<p data-bool><span data-bool><strong data-bool>value</strong></span></p>',
......
......@@ -92,12 +92,10 @@ final class EmailViewHelper extends AbstractTagBasedViewHelper
$linkText = (string)$linkResult->getLinkText();
$attributes = $linkResult->getAttributes();
unset($attributes['href']);
if ($frontend->spamProtectEmailAddresses !== 'ascii') {
if (PHP_VERSION_ID < 80100) {
$linkText = htmlspecialchars($linkText, ENT_COMPAT | ENT_SUBSTITUTE, 'utf-8', false);
} else {
$linkText = htmlspecialchars($linkText, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'utf-8', false);
}
if (PHP_VERSION_ID < 80100) {
$linkText = htmlspecialchars($linkText, ENT_COMPAT | ENT_SUBSTITUTE, 'utf-8', false);
} else {
$linkText = htmlspecialchars($linkText, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'utf-8', false);
}
}
}
......
......@@ -66,11 +66,6 @@ class EmailViewHelperTest extends FunctionalTestCase
1,
'<a href="#" data-mailto-token="nbjmup+tpnfAfnbjm/ume" data-mailto-vector="1">some(at)email.tld</a>',
],
'Plain email with ascii spam protection' => [
'<f:link.email email="some@email.tld" />',
'ascii',
'<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#115;&#111;&#109;&#101;&#64;&#101;&#109;&#97;&#105;&#108;&#46;&#116;&#108;&#100;">some(at)email.tld</a>',
],
'Susceptible email' => [
'<f:link.email email="\"><script>alert(\'email\')</script>" />',
0,
......@@ -93,17 +88,6 @@ class EmailViewHelperTest extends FunctionalTestCase
// for php 8.1
: '<a href="#" data-mailto-token="nbjmup+&quot;&gt;&lt;tdsjqu&gt;bmfsu(&#039;fnbjm&#039;)&lt;0tdsjqu&gt;" data-mailto-vector="1">&quot;&gt;&lt;script&gt;alert(&#039;email&#039;)&lt;/script&gt;</a>'),
],
'Susceptible email with ascii spam protection' => [
'<f:link.email email="\"><script>alert(\'email\')</script>" />',
'ascii',
// check against correct value regarding php 8.1 change of default argument values of flags for ex. htmlspecialchars()
// @todo remove conditional values when php 8.1 is min requirement
(PHP_VERSION_ID < 80100
// before php 8.1 - remove this for >php8.1 only
? '<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#34;&#62;&#60;&#115;&#99;&#114;&#105;&#112;&#116;&#62;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#101;&#109;&#97;&#105;&#108;&#39;&#41;&#60;&#47;&#115;&#99;&#114;&#105;&#112;&#116;&#62;">&quot;&gt;&lt;script&gt;alert(\'email\')&lt;/script&gt;</a>'
// for php 8.1
: '<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#34;&#62;&#60;&#115;&#99;&#114;&#105;&#112;&#116;&#62;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#101;&#109;&#97;&#105;&#108;&#39;&#41;&#60;&#47;&#115;&#99;&#114;&#105;&#112;&#116;&#62;">&quot;&gt;&lt;script&gt;alert(&#039;email&#039;)&lt;/script&gt;</a>'),
],
];
}
......
......@@ -4708,11 +4708,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
$JSwindowParams = implode(',', $JSwindow_paramsArr);
}
if (!$JSwindowParams && $linkedResult->getType() === LinkService::TYPE_EMAIL && $tsfe instanceof TypoScriptFrontendController && $tsfe->spamProtectEmailAddresses === 'ascii') {
$tagAttributes['href'] = $finalTagParts['url'];
} else {
$tagAttributes['href'] = htmlspecialchars($finalTagParts['url']);
}
$tagAttributes['href'] = htmlspecialchars($finalTagParts['url']);
if (!empty($title)) {
$tagAttributes['title'] = htmlspecialchars($title);
$this->lastTypoLinkResult = $this->lastTypoLinkResult->withAttribute('title', $title);
......@@ -5016,14 +5012,12 @@ class ContentObjectRenderer implements LoggerAwareInterface
if ($mailToUrl === $originalMailToUrl) {
$tsfe = $this->getTypoScriptFrontendController();
if ($tsfe instanceof TypoScriptFrontendController && $tsfe->spamProtectEmailAddresses) {
$mailToUrl = $this->encryptEmail($mailToUrl, $tsfe->spamProtectEmailAddresses);
if ($tsfe->spamProtectEmailAddresses !== 'ascii') {
$attributes = [
'data-mailto-token' => $mailToUrl,
'data-mailto-vector' => (int)$tsfe->spamProtectEmailAddresses,
];
$mailToUrl = '#';
}
$mailToUrl = $this->encryptEmail($mailToUrl, (int)$tsfe->spamProtectEmailAddresses);
$attributes = [
'data-mailto-token' => $mailToUrl,
'data-mailto-vector' => (int)$tsfe->spamProtectEmailAddresses,
];
$mailToUrl = '#';
$atLabel = '(at)';
if (($atLabelFromConfig = trim($tsfe->config['config']['spamProtectEmailAddresses_atSubst'] ?? '')) !== '') {
$atLabel = $atLabelFromConfig;
......@@ -5050,35 +5044,28 @@ class ContentObjectRenderer implements LoggerAwareInterface
* Encryption of email addresses for <A>-tags See the spam protection setup in TS 'config.'
*
* @param string $string Input string to en/decode: "mailto:some@example.com
* @param mixed $type - either "ascii" or a number between -10 and 10, taken from config.spamProtectEmailAddresses
* @param int $type a number between -10 and 10, taken from config.spamProtectEmailAddresses
* @return string encoded version of $string
*/
protected function encryptEmail(string $string, $type): string
protected function encryptEmail(string $string, int $type): string
{
$out = '';
// obfuscates using the decimal HTML entity references for each character
if ($type === 'ascii') {
foreach (preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY) as $char) {
$out .= '&#' . mb_ord($char) . ';';
}
} else {
// like str_rot13() but with a variable offset and a wider character range
$len = strlen($string);
$offset = (int)$type;
for ($i = 0; $i < $len; $i++) {
$charValue = ord($string[$i]);
// 0-9 . , - + / :
if ($charValue >= 43 && $charValue <= 58) {
$out .= $this->encryptCharcode($charValue, 43, 58, $offset);
} elseif ($charValue >= 64 && $charValue <= 90) {
// A-Z @
$out .= $this->encryptCharcode($charValue, 64, 90, $offset);
} elseif ($charValue >= 97 && $charValue <= 122) {
// a-z
$out .= $this->encryptCharcode($charValue, 97, 122, $offset);
} else {
$out .= $string[$i];
}
// like str_rot13() but with a variable offset and a wider character range
$len = strlen($string);
$offset = $type;
for ($i = 0; $i < $len; $i++) {
$charValue = ord($string[$i]);
// 0-9 . , - + / :
if ($charValue >= 43 && $charValue <= 58) {
$out .= $this->encryptCharcode($charValue, 43, 58, $offset);
} elseif ($charValue >= 64 && $charValue <= 90) {
// A-Z @
$out .= $this->encryptCharcode($charValue, 64, 90, $offset);
} elseif ($charValue >= 97 && $charValue <= 122) {
// a-z
$out .= $this->encryptCharcode($charValue, 97, 122, $offset);
} else {
$out .= $string[$i];
}
}
return $out;
......
......@@ -357,9 +357,9 @@ class TypoScriptFrontendController implements LoggerAwareInterface
/**
* If set, typolink() function encrypts email addresses.
* @var string|int
* @var int
*/
public $spamProtectEmailAddresses = 0;
public int $spamProtectEmailAddresses = 0;
/**
* Absolute Reference prefix
......@@ -2440,10 +2440,12 @@ class TypoScriptFrontendController implements LoggerAwareInterface
$this->intTarget = (string)($this->config['config']['intTarget'] ?? '');
$this->extTarget = (string)($this->config['config']['extTarget'] ?? '');
$this->fileTarget = (string)($this->config['config']['fileTarget'] ?? '');
$this->spamProtectEmailAddresses = $this->config['config']['spamProtectEmailAddresses'] ?? 0;
if ($this->spamProtectEmailAddresses !== 'ascii') {
$this->spamProtectEmailAddresses = MathUtility::forceIntegerInRange($this->spamProtectEmailAddresses, -10, 10, 0);
if (($this->config['config']['spamProtectEmailAddresses'] ?? '') === 'ascii') {
$this->logDeprecatedTyposcript('config.spamProtectEmailAddresses = ascii', 'This setting has no effect anymore. Change it to a number between -10 and 10 or remove it completely');
$this->config['config']['spamProtectEmailAddresses'] = 0;
}
$this->spamProtectEmailAddresses = (int)($this->config['config']['spamProtectEmailAddresses'] ?? 0);
$this->spamProtectEmailAddresses = MathUtility::forceIntegerInRange($this->spamProtectEmailAddresses, -10, 10, 0);
// calculate the absolute path prefix
if (!empty($this->absRefPrefix = trim($this->config['config']['absRefPrefix'] ?? ''))) {
if ($this->absRefPrefix === 'auto') {
......
......@@ -516,10 +516,6 @@ class ContentObjectRendererTest extends FunctionalTestCase
$tsfe->spamProtectEmailAddresses = 1;
$result = $subject->typoLink('Send me an email', ['parameter' => 'mailto:test@example.com']);
self::assertEquals('<a href="#" data-mailto-token="nbjmup+uftuAfybnqmf/dpn" data-mailto-vector="1">Send me an email</a>', $result);
$tsfe->spamProtectEmailAddresses = 'ascii';
$result = $subject->typoLink('Send me an email', ['parameter' => 'mailto:test@example.com']);
self::assertEquals('<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#116;&#101;&#115;&#116;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;">Send me an email</a>', $result);
}
/**
......
......@@ -21,10 +21,8 @@ use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
use TYPO3\CMS\Core\TypoScript\TemplateService;
use TYPO3\CMS\Frontend\Tests\Functional\SiteHandling\Fixtures\LinkHandlingController;
use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\Scenario\DataHandlerFactory;
use TYPO3\TestingFramework\Core\Functional\Framework\DataHandling\Scenario\DataHandlerWriter;
use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\Internal\ArrayValueInstruction;
use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\Internal\TypoScriptInstruction;
use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest;
use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequestContext;
......@@ -672,38 +670,6 @@ class SlugLinkGeneratorTest extends AbstractTestCase
self::assertSame($expectation, (string)$response->getBody());
}
/**
* @test
*/
public function linkIsGeneratedForExternalUrlOfEmailWithSpamProtectedEmailAddressesSetToAscii(): void
{
$response = $this->executeFrontendSubRequest(
(new InternalRequest('https://blog.acme.com/'))
->withPageId(2100)
->withInstructions([
(new TypoScriptInstruction(TemplateService::class))
->withTypoScript([
'config.' => [
'spamProtectEmailAddresses' => 'ascii',
],
]),
(new ArrayValueInstruction(LinkHandlingController::class))
->withArray([
'10' => 'TEXT',
'10.' => [
'typolink.' => [
'parameter' => 2910,
],
],
]),
]),
);
$result = (string)$response->getBody();
$expectation = '<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#121;&#111;&#117;&#45;&#110;&#101;&#118;&#101;&#114;&#45;&#103;&#101;&#116;&#45;&#97;&#110;&#45;&#97;&#110;&#115;&#119;&#101;&#114;">Contact wikipedia</a>';
self::assertSame($expectation, $result);
}
/**
* @return array
*/
......
......@@ -2160,7 +2160,7 @@ class ContentObjectRendererTest extends UnitTestCase
return [
'plain mail without mailto scheme' => [
[
'spamProtectEmailAddresses' => '',
'spamProtectEmailAddresses' => 0,
'spamProtectEmailAddresses_atSubst' => '',
'spamProtectEmailAddresses_lastDotSubst' => '',
],
......@@ -2170,7 +2170,7 @@ class ContentObjectRendererTest extends UnitTestCase
],
'plain mail with mailto scheme' => [
[
'spamProtectEmailAddresses' => '',
'spamProtectEmailAddresses' => 0,
'spamProtectEmailAddresses_atSubst' => '',
'spamProtectEmailAddresses_lastDotSubst' => '',
],
......@@ -2180,7 +2180,7 @@ class ContentObjectRendererTest extends UnitTestCase
],
'plain with at and dot substitution' => [
[
'spamProtectEmailAddresses' => '0',
'spamProtectEmailAddresses' => 0,
'spamProtectEmailAddresses_atSubst' => '(at)',
'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
],
......@@ -2190,7 +2190,7 @@ class ContentObjectRendererTest extends UnitTestCase
],
'mono-alphabetic substitution offset +1' => [
[
'spamProtectEmailAddresses' => '1',
'spamProtectEmailAddresses' => 1,
'spamProtectEmailAddresses_atSubst' => '',
'spamProtectEmailAddresses_lastDotSubst' => '',
],
......@@ -2200,7 +2200,7 @@ class ContentObjectRendererTest extends UnitTestCase
],
'mono-alphabetic substitution offset +1 with at substitution' => [
[
'spamProtectEmailAddresses' => '1',
'spamProtectEmailAddresses' => 1,
'spamProtectEmailAddresses_atSubst' => '@',
'spamProtectEmailAddresses_lastDotSubst' => '',
],
......@@ -2210,7 +2210,7 @@ class ContentObjectRendererTest extends UnitTestCase
],
'mono-alphabetic substitution offset +1 with at and dot substitution' => [
[
'spamProtectEmailAddresses' => '1',
'spamProtectEmailAddresses' => 1,
'spamProtectEmailAddresses_atSubst' => '(at)',
'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
],
......@@ -2220,7 +2220,7 @@ class ContentObjectRendererTest extends UnitTestCase
],
'mono-alphabetic substitution offset -1 with at and dot substitution' => [
[
'spamProtectEmailAddresses' => '-1',
'spamProtectEmailAddresses' => -1,
'spamProtectEmailAddresses_atSubst' => '(at)',
'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
],
......@@ -2230,7 +2230,7 @@ class ContentObjectRendererTest extends UnitTestCase
],
'mono-alphabetic substitution offset 2 with at and dot substitution and encoded subject' => [
[
'spamProtectEmailAddresses' => '2',
'spamProtectEmailAddresses' => 2,
'spamProtectEmailAddresses_atSubst' => '(at)',
'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
],
......@@ -2238,26 +2238,6 @@ class ContentObjectRendererTest extends UnitTestCase
'mailto:some.body@test.typo3.org?subject=foo%20bar',
'<a href="#" data-mailto-token="ocknvq,uqog0dqfaBvguv0varq50qti?uwdlgev=hqq%42dct" data-mailto-vector="2">some.body@test.typo3.org</a>',
],
'entity substitution with at and dot substitution' => [
[
'spamProtectEmailAddresses' => 'ascii',
'spamProtectEmailAddresses_atSubst' => '',
'spamProtectEmailAddresses_lastDotSubst' => '',
],
'some.body@test.typo3.org',
'mailto:some.body@test.typo3.org',
'<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#115;&#111;&#109;&#101;&#46;&#98;&#111;&#100;&#121;&#64;&#116;&#101;&#115;&#116;&#46;&#116;&#121;&#112;&#111;&#51;&#46;&#111;&#114;&#103;">some.body(at)test.typo3.org</a>',
],
'entity substitution with at and dot substitution with at and dot substitution' => [
[
'spamProtectEmailAddresses' => 'ascii',
'spamProtectEmailAddresses_atSubst' => '(at)',
'spamProtectEmailAddresses_lastDotSubst' => '(dot)',
],
'some.body@test.typo3.org',
'mailto:some.body@test.typo3.org',
'<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#115;&#111;&#109;&#101;&#46;&#98;&#111;&#100;&#121;&#64;&#116;&#101;&#115;&#116;&#46;&#116;&#121;&#112;&#111;&#51;&#46;&#111;&#114;&#103;">some.body(at)test.typo3(dot)org</a>',
],
];
}
......@@ -7562,50 +7542,6 @@ class ContentObjectRendererTest extends UnitTestCase
self::assertEquals($value, $this->subject->getUserObjectType());
}
/**
* Data provider for emailSpamProtectionWithTypeAscii
*
* @return array [$content, $expect]
*/
public function emailSpamProtectionWithTypeAsciiDataProvider(): array
{
return [
'Simple email address' => [
'test@email.tld',
'&#116;&#101;&#115;&#116;&#64;&#101;&#109;&#97;&#105;&#108;&#46;&#116;&#108;&#100;',
],
'Simple email address with unicode characters' => [
'matthäus@email.tld',
'&#109;&#97;&#116;&#116;&#104;&#228;&#117;&#115;&#64;&#101;&#109;&#97;&#105;&#108;&#46;&#116;&#108;&#100;',
],
'Susceptible email address' => [
'"><script>alert(\'emailSpamProtection\')</script>',
'&#34;&#62;&#60;&#115;&#99;&#114;&#105;&#112;&#116;&#62;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#101;&#109;&#97;&#105;&#108;&#83;&#112;&#97;&#109;&#80;&#114;&#111;&#116;&#101;&#99;&#116;&#105;&#111;&#110;&#39;&#41;&#60;&#47;&#115;&#99;&#114;&#105;&#112;&#116;&#62;',
],
'Susceptible email address with unicode characters' => [
'"><script>alert(\'ȅmǡilSpamProtȅction\')</script>',
'&#34;&#62;&#60;&#115;&#99;&#114;&#105;&#112;&#116;&#62;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#517;&#109;&#481;&#105;&#108;&#83;&#112;&#97;&#109;&#80;&#114;&#111;&#116;&#517;&#99;&#116;&#105;&#111;&#110;&#39;&#41;&#60;&#47;&#115;&#99;&#114;&#105;&#112;&#116;&#62;',
],
];
}
/**
* Check if email spam protection processes all UTF-8 characters properly
*
* @test
* @dataProvider emailSpamProtectionWithTypeAsciiDataProvider
* @param string $content The parameter $content.
* @param string $expected The expected output.
*/
public function mailSpamProtectionWithTypeAscii(string $content, string $expected): void
{
self::assertSame(
$expected,
$this->subject->_call('encryptEmail', $content, 'ascii')
);
}
public function getGlobalDataProvider(): array
{
return [
......
......@@ -646,24 +646,16 @@ Another way to solve the problem is using this option in combination with disabl
<default><![CDATA[
]]></default>
</property>
<property name="spamProtectEmailAddresses" type="string">
<description><![CDATA["ascii" / -10 to 10
<property name="spamProtectEmailAddresses" type="int">
<description><![CDATA[-10 to 10
If set, then all email addresses in typolinks will be encrypted so spam bots cannot detect them.
If you set this value to a number, then the encryption is simply an
If you set this value, then the encryption is simply an
offset of character values. If you set this value to "-2" then all
characters will have their ASCII value offset by "-2". To make this
possible, a little JavaScript code is added to every generated web page!
(It is recommended to set the value in the range from -5 to 1 since setting it to >= 2 means a "z" is converted to "|" which is a special character in TYPO3 tables syntax – and that might confuse columns in tables. Now hardcoded range)
Alternatively you can set this value to the keyword "ascii". This way every
character of the "mailto:" address will be translated to a Unicode HTML
notation. Have a look at the example to see how this works.
Example:
mailto:a@b.c will be converted to
mailto:&#97;&#64;&#98;&#46;&#99;
The big advantage of this method is that it doesn't need any JavaScript!]]></description>
]]></description>
<default><![CDATA[
]]></default>
</property>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment