diff --git a/Controller/CaptchaHandlerController.php b/Controller/CaptchaHandlerController.php index 2cbb180..4570aab 100644 --- a/Controller/CaptchaHandlerController.php +++ b/Controller/CaptchaHandlerController.php @@ -2,14 +2,21 @@ namespace Captcha\Bundle\CaptchaBundle\Controller; +use BDC_CaptchaBase; +use BDC_CaptchaHttpCommand; +use BDC_CaptchaScriptsHelper; +use BDC_CryptoHelper; +use BDC_HttpHelper; +use BDC_StringHelper; use Captcha\Bundle\CaptchaBundle\Support\Path; use Captcha\Bundle\CaptchaBundle\Support\LibraryLoader; use Captcha\Bundle\CaptchaBundle\Helpers\BotDetectCaptchaHelper; +use SoundRegenerationMode; use Symfony\Component\HttpFoundation\Response; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -class CaptchaHandlerController extends Controller +class CaptchaHandlerController extends AbstractController { /** * @var object @@ -33,31 +40,31 @@ public function indexAction() } $commandString = $this->getUrlParameter('get'); - if (!\BDC_StringHelper::HasValue($commandString)) { - \BDC_HttpHelper::BadRequest('command'); + if (!BDC_StringHelper::HasValue($commandString)) { + BDC_HttpHelper::BadRequest('command'); } - $commandString = \BDC_StringHelper::Normalize($commandString); - $command = \BDC_CaptchaHttpCommand::FromQuerystring($commandString); + $commandString = BDC_StringHelper::Normalize($commandString); + $command = BDC_CaptchaHttpCommand::FromQuerystring($commandString); $responseBody = ''; switch ($command) { - case \BDC_CaptchaHttpCommand::GetImage: + case BDC_CaptchaHttpCommand::GetImage: $responseBody = $this->getImage(); break; - case \BDC_CaptchaHttpCommand::GetSound: + case BDC_CaptchaHttpCommand::GetSound: $responseBody = $this->getSound(); break; - case \BDC_CaptchaHttpCommand::GetValidationResult: + case BDC_CaptchaHttpCommand::GetValidationResult: $responseBody = $this->getValidationResult(); break; - case \BDC_CaptchaHttpCommand::GetScriptInclude: + case BDC_CaptchaHttpCommand::GetScriptInclude: $responseBody = $this->getScriptInclude(); break; - case \BDC_CaptchaHttpCommand::GetP: + case BDC_CaptchaHttpCommand::GetP: $responseBody = $this->getP(); break; default: - \BDC_HttpHelper::BadRequest('command'); + BDC_HttpHelper::BadRequest('command'); break; } @@ -105,7 +112,7 @@ public function getResourceContents() throw new BadRequestHttpException('Invalid file name.'); } - $resourcePath = realpath(Path::getPublicDirPathInLibrary($this->container) . $filename); + $resourcePath = realpath(Path::getPublicDirPathInLibrary() . $filename); if (!is_file($resourcePath)) { throw new BadRequestHttpException(sprintf('File "%s" could not be found.', $filename)); @@ -129,20 +136,20 @@ public function getResourceContents() public function getImage() { if (is_null($this->captcha)) { - \BDC_HttpHelper::BadRequest('captcha'); + BDC_HttpHelper::BadRequest('captcha'); } // identifier of the particular Captcha object instance $instanceId = $this->getInstanceId(); if (is_null($instanceId)) { - \BDC_HttpHelper::BadRequest('instance'); + BDC_HttpHelper::BadRequest('instance'); } // image generation invalidates sound cache, if any $this->clearSoundData($instanceId); // response headers - \BDC_HttpHelper::DisallowCache(); + BDC_HttpHelper::DisallowCache(); // response MIME type & headers $mimeType = $this->captcha->CaptchaBase->ImageMimeType; @@ -167,26 +174,26 @@ public function getImage() public function getSound() { if (is_null($this->captcha)) { - \BDC_HttpHelper::BadRequest('captcha'); + BDC_HttpHelper::BadRequest('captcha'); } // identifier of the particular Captcha object instance $instanceId = $this->getInstanceId(); if (is_null($instanceId)) { - \BDC_HttpHelper::BadRequest('instance'); + BDC_HttpHelper::BadRequest('instance'); } $soundBytes = $this->getSoundData($this->captcha, $instanceId); if (is_null($soundBytes)) { - \BDC_HttpHelper::BadRequest('Please reload the form page before requesting another Captcha sound'); + BDC_HttpHelper::BadRequest('Please reload the form page before requesting another Captcha sound'); exit; } $totalSize = strlen($soundBytes); // response headers - \BDC_HttpHelper::SmartDisallowCache(); + BDC_HttpHelper::SmartDisallowCache(); // response MIME type & headers $mimeType = $this->captcha->CaptchaBase->SoundMimeType; @@ -194,7 +201,7 @@ public function getSound() header('Content-Transfer-Encoding: binary'); if (!array_key_exists('d', $_GET)) { // javascript player not used, we send the file directly as a download - $downloadId = \BDC_CryptoHelper::GenerateGuid(); + $downloadId = BDC_CryptoHelper::GenerateGuid(); header("Content-Disposition: attachment; filename=captcha_{$downloadId}.wav"); } @@ -210,7 +217,7 @@ public function getSound() // end of sound playback, cleanup and tell AppleCoreMedia to stop requesting // invalid "bytes=rangeEnd-rangeEnd" ranges in an infinite(?) loop if ($rangeStart == $rangeEnd || $rangeEnd > $totalSize) { - \BDC_HttpHelper::BadRequest('invalid byte range'); + BDC_HttpHelper::BadRequest('invalid byte range'); } $rangeBytes = substr($soundBytes, $rangeStart, $rangeSize); @@ -238,7 +245,7 @@ public function getSound() public function getSoundData($p_Captcha, $p_InstanceId) { $shouldCache = ( - ($p_Captcha->SoundRegenerationMode == \SoundRegenerationMode::None) || // no sound regeneration allowed, so we must cache the first and only generated sound + ($p_Captcha->SoundRegenerationMode == SoundRegenerationMode::None) || // no sound regeneration allowed, so we must cache the first and only generated sound $this->detectIosRangeRequest() // keep the same Captcha sound across all chunked iOS requests ); @@ -286,20 +293,20 @@ private function clearSoundData($p_InstanceId) private function detectIosRangeRequest() { if (array_key_exists('HTTP_RANGE', $_SERVER) && - \BDC_StringHelper::HasValue($_SERVER['HTTP_RANGE'])) { + BDC_StringHelper::HasValue($_SERVER['HTTP_RANGE'])) { // Safari on MacOS and all browsers on <= iOS 10.x if (array_key_exists('HTTP_X_PLAYBACK_SESSION_ID', $_SERVER) && - \BDC_StringHelper::HasValue($_SERVER['HTTP_X_PLAYBACK_SESSION_ID'])) { + BDC_StringHelper::HasValue($_SERVER['HTTP_X_PLAYBACK_SESSION_ID'])) { return true; } $userAgent = array_key_exists('HTTP_USER_AGENT', $_SERVER) ? $_SERVER['HTTP_USER_AGENT'] : null; // all browsers on iOS 11.x and later - if(\BDC_StringHelper::HasValue($userAgent)) { - $userAgentLC = \BDC_StringHelper::Lowercase($userAgent); - if (\BDC_StringHelper::Contains($userAgentLC, "like mac os") || \BDC_StringHelper::Contains($userAgentLC, "like macos")) { + if(BDC_StringHelper::HasValue($userAgent)) { + $userAgentLC = BDC_StringHelper::Lowercase($userAgent); + if (BDC_StringHelper::Contains($userAgentLC, "like mac os") || BDC_StringHelper::Contains($userAgentLC, "like macos")) { return true; } } @@ -311,7 +318,7 @@ private function getSoundByteRange() { // chunked requests must include the desired byte range $rangeStr = $_SERVER['HTTP_RANGE']; - if (!\BDC_StringHelper::HasValue($rangeStr)) { + if (!BDC_StringHelper::HasValue($rangeStr)) { return; } @@ -328,7 +335,7 @@ private function detectFakeRangeRequest() $detected = false; if (array_key_exists('HTTP_RANGE', $_SERVER)) { $rangeStr = $_SERVER['HTTP_RANGE']; - if (\BDC_StringHelper::HasValue($rangeStr) && + if (BDC_StringHelper::HasValue($rangeStr) && preg_match('/bytes=0-$/', $rangeStr)) { $detected = true; } @@ -339,18 +346,18 @@ private function detectFakeRangeRequest() /** * The client requests the Captcha validation result (used for Ajax Captcha validation). * - * @return json + * @return string */ public function getValidationResult() { if (is_null($this->captcha)) { - \BDC_HttpHelper::BadRequest('captcha'); + BDC_HttpHelper::BadRequest('captcha'); } // identifier of the particular Captcha object instance $instanceId = $this->getInstanceId(); if (is_null($instanceId)) { - \BDC_HttpHelper::BadRequest('instance'); + BDC_HttpHelper::BadRequest('instance'); } $mimeType = 'application/json'; @@ -365,22 +372,20 @@ public function getValidationResult() $result = $this->captcha->AjaxValidate($userInput, $instanceId); $this->captcha->CaptchaBase->Save(); } - $resultJson = $this->getJsonValidationResult($result); - - return $resultJson; + return $this->getJsonValidationResult($result); } public function getScriptInclude() { // saved data for the specified Captcha object in the application if (is_null($this->captcha)) { - \BDC_HttpHelper::BadRequest('captcha'); + BDC_HttpHelper::BadRequest('captcha'); } // identifier of the particular Captcha object instance $instanceId = $this->getInstanceId(); if (is_null($instanceId)) { - \BDC_HttpHelper::BadRequest('instance'); + BDC_HttpHelper::BadRequest('instance'); } // response MIME type & headers @@ -388,7 +393,7 @@ public function getScriptInclude() header('X-Robots-Tag: noindex, nofollow, noarchive, nosnippet'); // 1. load BotDetect script - $resourcePath = realpath(Path::getPublicDirPathInLibrary($this->container) . 'bdc-traditional-api-script-include.js'); + $resourcePath = realpath(Path::getPublicDirPathInLibrary() . 'bdc-traditional-api-script-include.js'); if (!is_file($resourcePath)) { throw new BadRequestHttpException(sprintf('File "%s" could not be found.', $resourcePath)); @@ -397,12 +402,12 @@ public function getScriptInclude() $script = file_get_contents($resourcePath); // 2. load BotDetect Init script - $script .= \BDC_CaptchaScriptsHelper::GetInitScriptMarkup($this->captcha, $instanceId); + $script .= BDC_CaptchaScriptsHelper::GetInitScriptMarkup($this->captcha, $instanceId); // add remote scripts if enabled if ($this->captcha->RemoteScriptEnabled) { $script .= "\r\n"; - $script .= \BDC_CaptchaScriptsHelper::GetRemoteScript($this->captcha); + $script .= BDC_CaptchaScriptsHelper::GetRemoteScript($this->captcha); } return $script; @@ -414,8 +419,8 @@ public function getScriptInclude() private function getInstanceId() { $instanceId = $this->getUrlParameter('t'); - if (!\BDC_StringHelper::HasValue($instanceId) || - !\BDC_CaptchaBase::IsValidInstanceId($instanceId) + if (!BDC_StringHelper::HasValue($instanceId) || + !BDC_CaptchaBase::IsValidInstanceId($instanceId) ) { return; } @@ -450,12 +455,12 @@ private function getUserInput() /** * Encodes the Captcha validation result in a simple JSON wrapper. * + * @param $result * @return string */ private function getJsonValidationResult($result) { - $resultStr = ($result ? 'true': 'false'); - return $resultStr; + return ($result ? 'true': 'false'); } /** @@ -478,13 +483,13 @@ private function getUrlParameter($param) public function getP() { if (is_null($this->captcha)) { - \BDC_HttpHelper::BadRequest('captcha'); + BDC_HttpHelper::BadRequest('captcha'); } // identifier of the particular Captcha object instance $instanceId = $this->getInstanceId(); if (is_null($instanceId)) { - \BDC_HttpHelper::BadRequest('instance'); + BDC_HttpHelper::BadRequest('instance'); } // create new one @@ -497,7 +502,7 @@ public function getP() // response MIME type & headers header('Content-Type: application/json'); header('X-Robots-Tag: noindex, nofollow, noarchive, nosnippet'); - \BDC_HttpHelper::SmartDisallowCache(); + BDC_HttpHelper::SmartDisallowCache(); return $response; } diff --git a/Controller/SimpleCaptchaHandlerController.php b/Controller/SimpleCaptchaHandlerController.php index 37ac9c8..b398305 100644 --- a/Controller/SimpleCaptchaHandlerController.php +++ b/Controller/SimpleCaptchaHandlerController.php @@ -2,14 +2,22 @@ namespace Captcha\Bundle\CaptchaBundle\Controller; +use BDC_HttpHelper; +use BDC_SimpleCaptchaBase; +use BDC_SimpleCaptchaHttpCommand; +use BDC_SimpleCaptchaScriptsHelper; +use BDC_StringHelper; use Captcha\Bundle\CaptchaBundle\Support\Path; use Captcha\Bundle\CaptchaBundle\Support\SimpleLibraryLoader; use Captcha\Bundle\CaptchaBundle\Helpers\BotDetectSimpleCaptchaHelper; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use CorsAuth; +use ImageFormat; +use SimpleCaptchaRequestValidator; +use SoundRegenerationMode; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; -class SimpleCaptchaHandlerController extends Controller +class SimpleCaptchaHandlerController extends AbstractController { /** * @var object @@ -24,70 +32,70 @@ public function indexAction() $this->captcha = $this->getBotDetectCaptchaInstance(); $commandString = $this->getUrlParameter('get'); - if (!\BDC_StringHelper::HasValue($commandString)) { - \BDC_HttpHelper::BadRequest('command'); + if (!BDC_StringHelper::HasValue($commandString)) { + BDC_HttpHelper::BadRequest('command'); } - $commandString = \BDC_StringHelper::Normalize($commandString); - $command = \BDC_SimpleCaptchaHttpCommand::FromQuerystring($commandString); + $commandString = BDC_StringHelper::Normalize($commandString); + $command = BDC_SimpleCaptchaHttpCommand::FromQuerystring($commandString); $responseBody = ''; switch ($command) { - case \BDC_SimpleCaptchaHttpCommand::GetImage: + case BDC_SimpleCaptchaHttpCommand::GetImage: $responseBody = $this->getImage(); break; - case \BDC_SimpleCaptchaHttpCommand::GetBase64ImageString: + case BDC_SimpleCaptchaHttpCommand::GetBase64ImageString: $responseBody = $this->getBase64ImageString(); break; - case \BDC_SimpleCaptchaHttpCommand::GetSound: + case BDC_SimpleCaptchaHttpCommand::GetSound: $responseBody = $this->getSound(); break; - case \BDC_SimpleCaptchaHttpCommand::GetHtml: + case BDC_SimpleCaptchaHttpCommand::GetHtml: $responseBody = $this->getHtml(); break; - case \BDC_SimpleCaptchaHttpCommand::GetValidationResult: + case BDC_SimpleCaptchaHttpCommand::GetValidationResult: $responseBody = $this->getValidationResult(); break; // Sound icon - case \BDC_SimpleCaptchaHttpCommand::GetSoundIcon: + case BDC_SimpleCaptchaHttpCommand::GetSoundIcon: $responseBody = $this->getSoundIcon(); break; - case \BDC_SimpleCaptchaHttpCommand::GetSoundSmallIcon: + case BDC_SimpleCaptchaHttpCommand::GetSoundSmallIcon: $responseBody = $this->getSmallSoundIcon(); break; - case \BDC_SimpleCaptchaHttpCommand::GetSoundDisabledIcon: + case BDC_SimpleCaptchaHttpCommand::GetSoundDisabledIcon: $responseBody = $this->getDisabledSoundIcon(); break; - case \BDC_SimpleCaptchaHttpCommand::GetSoundSmallDisabledIcon: + case BDC_SimpleCaptchaHttpCommand::GetSoundSmallDisabledIcon: $responseBody = $this->getSmallDisabledSoundIcon(); break; // Reload icon - case \BDC_SimpleCaptchaHttpCommand::GetReloadIcon: + case BDC_SimpleCaptchaHttpCommand::GetReloadIcon: $responseBody = $this->getReloadIcon(); break; - case \BDC_SimpleCaptchaHttpCommand::GetReloadSmallIcon: + case BDC_SimpleCaptchaHttpCommand::GetReloadSmallIcon: $responseBody = $this->getSmallReloadIcon(); break; - case \BDC_SimpleCaptchaHttpCommand::GetReloadDisabledIcon: + case BDC_SimpleCaptchaHttpCommand::GetReloadDisabledIcon: $responseBody = $this->getDisabledReloadIcon(); break; - case \BDC_SimpleCaptchaHttpCommand::GetReloadSmallDisabledIcon: + case BDC_SimpleCaptchaHttpCommand::GetReloadSmallDisabledIcon: $responseBody = $this->getSmallDisabledReloadIcon(); break; // css, js - case \BDC_SimpleCaptchaHttpCommand::GetScriptInclude: + case BDC_SimpleCaptchaHttpCommand::GetScriptInclude: $responseBody = $this->getScriptInclude(); break; - case \BDC_SimpleCaptchaHttpCommand::GetLayoutStyleSheet: + case BDC_SimpleCaptchaHttpCommand::GetLayoutStyleSheet: $responseBody = $this->getLayoutStyleSheet(); break; - case \BDC_SimpleCaptchaHttpCommand::GetP: + case BDC_SimpleCaptchaHttpCommand::GetP: $responseBody = $this->getP(); break; default: - \BDC_HttpHelper::BadRequest('command'); + BDC_HttpHelper::BadRequest('command'); break; } @@ -114,8 +122,8 @@ private function getBotDetectCaptchaInstance() $captchaId = $this->getUrlParameter('t'); if ($captchaId !== null) { - $captchaId = \BDC_StringHelper::Normalize($captchaId); - if (1 !== preg_match(\BDC_SimpleCaptchaBase::VALID_CAPTCHA_ID, $captchaId)) { + $captchaId = BDC_StringHelper::Normalize($captchaId); + if (1 !== preg_match(BDC_SimpleCaptchaBase::VALID_CAPTCHA_ID, $captchaId)) { return null; } } @@ -134,30 +142,30 @@ public function getImage() header("Access-Control-Allow-Origin: *"); // authenticate client-side request - $corsAuth = new \CorsAuth(); + $corsAuth = new CorsAuth(); if (!$corsAuth->IsClientAllowed()) { - \BDC_HttpHelper::BadRequest($corsAuth->GetFrontEnd() . " is not an allowed front-end"); + BDC_HttpHelper::BadRequest($corsAuth->GetFrontEnd() . " is not an allowed front-end"); return null; } if (is_null($this->captcha)) { - \BDC_HttpHelper::BadRequest('captcha'); + BDC_HttpHelper::BadRequest('captcha'); } // identifier of the particular Captcha object instance $captchaId = $this->getCaptchaId(); if (is_null($captchaId)) { - \BDC_HttpHelper::BadRequest('instance'); + BDC_HttpHelper::BadRequest('instance'); } // image generation invalidates sound cache, if any $this->clearSoundData($this->captcha, $captchaId); // response headers - \BDC_HttpHelper::DisallowCache(); + BDC_HttpHelper::DisallowCache(); // response MIME type & headers - $imageType = \ImageFormat::GetName($this->captcha->ImageFormat); + $imageType = ImageFormat::GetName($this->captcha->ImageFormat); $imageType = strtolower($imageType[0]); $mimeType = "image/" . $imageType; header("Content-Type: {$mimeType}"); @@ -179,22 +187,21 @@ public function getBase64ImageString() header("Access-Control-Allow-Origin: *"); // authenticate client-side request - $corsAuth = new \CorsAuth(); + $corsAuth = new CorsAuth(); if (!$corsAuth->IsClientAllowed()) { - \BDC_HttpHelper::BadRequest($corsAuth->GetFrontEnd() . " is not an allowed front-end"); + BDC_HttpHelper::BadRequest($corsAuth->GetFrontEnd() . " is not an allowed front-end"); return null; } // MIME type - $imageType = \ImageFormat::GetName($this->captcha->ImageFormat); + $imageType = ImageFormat::GetName($this->captcha->ImageFormat); $imageType = strtolower($imageType[0]); $mimeType = "image/" . $imageType; $rawImage = $this->getImageData($this->captcha); - $base64ImageString = sprintf('data:%s;base64,%s', $mimeType, base64_encode($rawImage)); - return $base64ImageString; + return sprintf('data:%s;base64,%s', $mimeType, base64_encode($rawImage)); } @@ -203,7 +210,7 @@ private function getImageData($p_Captcha) // identifier of the particular Captcha object instance $captchaId = $this->getCaptchaId(); if (is_null($captchaId)) { - \BDC_HttpHelper::BadRequest('Captcha Id doesn\'t exist'); + BDC_HttpHelper::BadRequest('Captcha Id doesn\'t exist'); } if ($this->isObviousBotRequest($p_Captcha)) { @@ -214,7 +221,7 @@ private function getImageData($p_Captcha) $this->clearSoundData($p_Captcha, $captchaId); // response headers - \BDC_HttpHelper::DisallowCache(); + BDC_HttpHelper::DisallowCache(); // we don't support content chunking, since image files // are regenerated randomly on each request @@ -238,20 +245,20 @@ public function getSound() header("Access-Control-Allow-Origin: *"); // authenticate client-side request - $corsAuth = new \CorsAuth(); + $corsAuth = new CorsAuth(); if (!$corsAuth->IsClientAllowed()) { - \BDC_HttpHelper::BadRequest($corsAuth->GetFrontEnd() . " is not an allowed front-end"); + BDC_HttpHelper::BadRequest($corsAuth->GetFrontEnd() . " is not an allowed front-end"); return null; } if (is_null($this->captcha)) { - \BDC_HttpHelper::BadRequest('captcha'); + BDC_HttpHelper::BadRequest('captcha'); } // identifier of the particular Captcha object instance $captchaId = $this->getCaptchaId(); if (is_null($captchaId)) { - \BDC_HttpHelper::BadRequest('Captcha Id doesn\'t exist'); + BDC_HttpHelper::BadRequest('Captcha Id doesn\'t exist'); } if ($this->isObviousBotRequest($this->captcha)) { @@ -261,14 +268,14 @@ public function getSound() $soundBytes = $this->getSoundData($this->captcha, $captchaId); if (is_null($soundBytes)) { - \BDC_HttpHelper::BadRequest('Please reload the form page before requesting another Captcha sound'); + BDC_HttpHelper::BadRequest('Please reload the form page before requesting another Captcha sound'); exit; } $totalSize = strlen($soundBytes); // response headers - \BDC_HttpHelper::SmartDisallowCache(); + BDC_HttpHelper::SmartDisallowCache(); // response MIME type & headers $mimeType = $this->captcha->CaptchaBase->SoundMimeType; @@ -292,7 +299,7 @@ public function getSound() // end of sound playback, cleanup and tell AppleCoreMedia to stop requesting // invalid "bytes=rangeEnd-rangeEnd" ranges in an infinite(?) loop if ($rangeStart == $rangeEnd || $rangeEnd > $totalSize) { - \BDC_HttpHelper::BadRequest('invalid byte range'); + BDC_HttpHelper::BadRequest('invalid byte range'); } $rangeBytes = substr($soundBytes, $rangeStart, $rangeSize); @@ -319,7 +326,7 @@ public function getSound() public function getSoundData($p_Captcha, $p_CaptchaId) { $shouldCache = ( - ($p_Captcha->SoundRegenerationMode == \SoundRegenerationMode::None) || // no sound regeneration allowed, so we must cache the first and only generated sound + ($p_Captcha->SoundRegenerationMode == SoundRegenerationMode::None) || // no sound regeneration allowed, so we must cache the first and only generated sound $this->detectIosRangeRequest() // keep the same Captcha sound across all chunked iOS requests ); @@ -368,20 +375,20 @@ private function clearSoundData($p_Captcha, $p_CaptchaId) private function detectIosRangeRequest() { if (array_key_exists('HTTP_RANGE', $_SERVER) && - \BDC_StringHelper::HasValue($_SERVER['HTTP_RANGE'])) { + BDC_StringHelper::HasValue($_SERVER['HTTP_RANGE'])) { // Safari on MacOS and all browsers on <= iOS 10.x if (array_key_exists('HTTP_X_PLAYBACK_SESSION_ID', $_SERVER) && - \BDC_StringHelper::HasValue($_SERVER['HTTP_X_PLAYBACK_SESSION_ID'])) { + BDC_StringHelper::HasValue($_SERVER['HTTP_X_PLAYBACK_SESSION_ID'])) { return true; } $userAgent = array_key_exists('HTTP_USER_AGENT', $_SERVER) ? $_SERVER['HTTP_USER_AGENT'] : null; // all browsers on iOS 11.x and later - if(\BDC_StringHelper::HasValue($userAgent)) { - $userAgentLC = \BDC_StringHelper::Lowercase($userAgent); - if (\BDC_StringHelper::Contains($userAgentLC, "like mac os") || \BDC_StringHelper::Contains($userAgentLC, "like macos")) { + if(BDC_StringHelper::HasValue($userAgent)) { + $userAgentLC = BDC_StringHelper::Lowercase($userAgent); + if (BDC_StringHelper::Contains($userAgentLC, "like mac os") || BDC_StringHelper::Contains($userAgentLC, "like macos")) { return true; } } @@ -393,7 +400,7 @@ private function getSoundByteRange() { // chunked requests must include the desired byte range $rangeStr = $_SERVER['HTTP_RANGE']; - if (!\BDC_StringHelper::HasValue($rangeStr)) { + if (!BDC_StringHelper::HasValue($rangeStr)) { return; } @@ -410,7 +417,7 @@ private function detectFakeRangeRequest() $detected = false; if (array_key_exists('HTTP_RANGE', $_SERVER)) { $rangeStr = $_SERVER['HTTP_RANGE']; - if (\BDC_StringHelper::HasValue($rangeStr) && + if (BDC_StringHelper::HasValue($rangeStr) && preg_match('/bytes=0-$/', $rangeStr)) { $detected = true; } @@ -422,13 +429,12 @@ public function getHtml() { header("Access-Control-Allow-Origin: *"); - $corsAuth = new \CorsAuth(); + $corsAuth = new CorsAuth(); if (!$corsAuth->IsClientAllowed()) { - \BDC_HttpHelper::BadRequest($corsAuth->GetFrontEnd() . " is not an allowed front-end"); + BDC_HttpHelper::BadRequest($corsAuth->GetFrontEnd() . " is not an allowed front-end"); } - $html = "
" . $this->captcha->Html() . "
"; - return $html; + return "
" . $this->captcha->Html() . "
"; } /** @@ -441,20 +447,20 @@ public function getValidationResult() header("Access-Control-Allow-Origin: *"); // authenticate client-side request - $corsAuth = new \CorsAuth(); + $corsAuth = new CorsAuth(); if (!$corsAuth->IsClientAllowed()) { - \BDC_HttpHelper::BadRequest($corsAuth->GetFrontEnd() . " is not an allowed front-end"); + BDC_HttpHelper::BadRequest($corsAuth->GetFrontEnd() . " is not an allowed front-end"); return null; } if (is_null($this->captcha)) { - \BDC_HttpHelper::BadRequest('captcha'); + BDC_HttpHelper::BadRequest('captcha'); } // identifier of the particular Captcha object instance $captchaId = $this->getCaptchaId(); if (is_null($captchaId)) { - \BDC_HttpHelper::BadRequest('instance'); + BDC_HttpHelper::BadRequest('instance'); } $mimeType = 'application/json'; @@ -468,64 +474,62 @@ public function getValidationResult() if (isset($userInput) && (isset($captchaId))) { $result = $this->captcha->AjaxValidate($userInput, $captchaId); } - $resultJson = $this->getJsonValidationResult($result); - - return $resultJson; + return $this->getJsonValidationResult($result); } // Get Reload Icon group public function getSoundIcon() { - $filePath = realpath(Path::getPublicDirPathInLibrary($this->container) . 'bdc-sound-icon.gif'); + $filePath = realpath(Path::getPublicDirPathInLibrary() . 'bdc-sound-icon.gif'); return $this->getWebResource($filePath, 'image/gif'); } public function getSmallSoundIcon() { - $filePath = realpath(Path::getPublicDirPathInLibrary($this->container) . 'bdc-sound-small-icon.gif'); + $filePath = realpath(Path::getPublicDirPathInLibrary() . 'bdc-sound-small-icon.gif'); return $this->getWebResource($filePath, 'image/gif'); } public function getDisabledSoundIcon() { - $filePath = realpath(Path::getPublicDirPathInLibrary($this->container) . 'bdc-sound-disabled-icon.gif'); + $filePath = realpath(Path::getPublicDirPathInLibrary() . 'bdc-sound-disabled-icon.gif'); return $this->getWebResource($filePath, 'image/gif'); } public function getSmallDisabledSoundIcon() { - $filePath = realpath(Path::getPublicDirPathInLibrary($this->container) . 'bdc-sound-small-disabled-icon.gif'); + $filePath = realpath(Path::getPublicDirPathInLibrary() . 'bdc-sound-small-disabled-icon.gif'); return $this->getWebResource($filePath, 'image/gif'); } // Get Reload Icon group public function getReloadIcon() { - $filePath = realpath(Path::getPublicDirPathInLibrary($this->container) . 'bdc-reload-icon.gif'); + $filePath = realpath(Path::getPublicDirPathInLibrary() . 'bdc-reload-icon.gif'); return $this->getWebResource($filePath, 'image/gif'); } public function getSmallReloadIcon() { - $filePath = realpath(Path::getPublicDirPathInLibrary($this->container) . 'bdc-reload-small-icon.gif'); + $filePath = realpath(Path::getPublicDirPathInLibrary() . 'bdc-reload-small-icon.gif'); return $this->getWebResource($filePath, 'image/gif'); } public function getDisabledReloadIcon() { - $filePath = realpath(Path::getPublicDirPathInLibrary($this->container) . 'bdc-reload-disabled-icon.gif'); + $filePath = realpath(Path::getPublicDirPathInLibrary() . 'bdc-reload-disabled-icon.gif'); return $this->getWebResource($filePath, 'image/gif'); } public function getSmallDisabledReloadIcon() { - $filePath = realpath(Path::getPublicDirPathInLibrary($this->container) . 'bdc-reload-small-disabled-icon.gif'); + $filePath = realpath(Path::getPublicDirPathInLibrary() . 'bdc-reload-small-disabled-icon.gif'); return $this->getWebResource($filePath, 'image/gif'); } public function getLayoutStyleSheet() { - $filePath = realpath(Path::getPublicDirPathInLibrary($this->container) . 'bdc-layout-stylesheet.css'); + $filePath = realpath(Path::getPublicDirPathInLibrary() . 'bdc-layout-stylesheet.css'); return $this->getWebResource($filePath, 'text/css'); } @@ -535,13 +539,13 @@ public function getScriptInclude() // saved data for the specified Captcha object in the application if (is_null($this->captcha)) { - \BDC_HttpHelper::BadRequest('captcha'); + BDC_HttpHelper::BadRequest('captcha'); } // identifier of the particular Captcha object instance $captchaId = $this->getCaptchaId(); if (is_null($captchaId)) { - \BDC_HttpHelper::BadRequest('instance'); + BDC_HttpHelper::BadRequest('instance'); } // response MIME type & headers @@ -549,7 +553,7 @@ public function getScriptInclude() header('X-Robots-Tag: noindex, nofollow, noarchive, nosnippet'); // 1. load BotDetect script - $resourcePath = realpath(Path::getPublicDirPathInLibrary($this->container) . 'bdc-simple-api-script-include.js'); + $resourcePath = realpath(Path::getPublicDirPathInLibrary() . 'bdc-simple-api-script-include.js'); if (!is_file($resourcePath)) { throw new BadRequestHttpException(sprintf('File "%s" could not be found.', $resourcePath)); @@ -558,12 +562,12 @@ public function getScriptInclude() $script = $this->getWebResource($resourcePath, 'text/javascript', false); // 2. load BotDetect Init script - $script .= \BDC_SimpleCaptchaScriptsHelper::GetInitScriptMarkup($this->captcha, $captchaId); + $script .= BDC_SimpleCaptchaScriptsHelper::GetInitScriptMarkup($this->captcha, $captchaId); // add remote scripts if enabled if ($this->captcha->RemoteScriptEnabled) { $script .= "\r\n"; - $script .= \BDC_SimpleCaptchaScriptsHelper::GetRemoteScript($this->captcha, $this->getClientSideFramework()); + $script .= BDC_SimpleCaptchaScriptsHelper::GetRemoteScript($this->captcha, $this->getClientSideFramework()); } return $script; @@ -572,8 +576,8 @@ public function getScriptInclude() private function getClientSideFramework() { $clientSide = $this->getUrlParameter('cs'); - if (\BDC_StringHelper::HasValue($clientSide)) { - $clientSide = \BDC_StringHelper::Normalize($clientSide); + if (BDC_StringHelper::HasValue($clientSide)) { + $clientSide = BDC_StringHelper::Normalize($clientSide); return $clientSide; } return null; @@ -583,7 +587,7 @@ private function getWebResource($p_Resource, $p_MimeType, $hasEtag = true) { header("Content-Type: $p_MimeType"); if ($hasEtag) { - \BDC_HttpHelper::AllowEtagCache($p_Resource); + BDC_HttpHelper::AllowEtagCache($p_Resource); } return file_get_contents($p_Resource); @@ -591,14 +595,14 @@ private function getWebResource($p_Resource, $p_MimeType, $hasEtag = true) private function isObviousBotRequest($p_Captcha) { - $captchaRequestValidator = new \SimpleCaptchaRequestValidator($p_Captcha->Configuration); + $captchaRequestValidator = new SimpleCaptchaRequestValidator($p_Captcha->Configuration); // some basic request checks $captchaRequestValidator->RecordRequest(); if ($captchaRequestValidator->IsObviousBotAttempt()) { - \BDC_HttpHelper::TooManyRequests('IsObviousBotAttempt'); + BDC_HttpHelper::TooManyRequests('IsObviousBotAttempt'); } return false; @@ -611,7 +615,7 @@ private function getCaptchaId() { $captchaId = $this->getUrlParameter('t'); - if (!\BDC_StringHelper::HasValue($captchaId)) { + if (!BDC_StringHelper::HasValue($captchaId)) { return null; } @@ -620,7 +624,7 @@ private function getCaptchaId() return null; } - if (1 !== preg_match(\BDC_SimpleCaptchaBase::VALID_CAPTCHA_ID, $captchaId)) { + if (1 !== preg_match(BDC_SimpleCaptchaBase::VALID_CAPTCHA_ID, $captchaId)) { return null; } @@ -655,12 +659,12 @@ private function getUserInput() /** * Encodes the Captcha validation result in a simple JSON wrapper. * + * @param $result * @return string */ private function getJsonValidationResult($result) { - $resultStr = ($result ? 'true': 'false'); - return $resultStr; + return ($result ? 'true': 'false'); } /** @@ -677,20 +681,20 @@ public function getP() header("Access-Control-Allow-Origin: *"); // authenticate client-side request - $corsAuth = new \CorsAuth(); + $corsAuth = new CorsAuth(); if (!$corsAuth->IsClientAllowed()) { - \BDC_HttpHelper::BadRequest($corsAuth->GetFrontEnd() . " is not an allowed front-end"); + BDC_HttpHelper::BadRequest($corsAuth->GetFrontEnd() . " is not an allowed front-end"); return null; } if (is_null($this->captcha)) { - \BDC_HttpHelper::BadRequest('captcha'); + BDC_HttpHelper::BadRequest('captcha'); } // identifier of the particular Captcha object instance $captchaId = $this->getCaptchaId(); if (is_null($captchaId)) { - \BDC_HttpHelper::BadRequest('instance'); + BDC_HttpHelper::BadRequest('instance'); } // create new one @@ -703,7 +707,7 @@ public function getP() // response MIME type & headers header('Content-Type: application/json'); header('X-Robots-Tag: noindex, nofollow, noarchive, nosnippet'); - \BDC_HttpHelper::SmartDisallowCache(); + BDC_HttpHelper::SmartDisallowCache(); return $response; } diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index b55c3c6..60f0024 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -15,10 +15,10 @@ public function getConfigTreeBuilder() { $captchaLibPathDefault = Path::getDefaultLibPackageDirPath(); - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('captcha'); + $treeBuilder = new TreeBuilder('captcha'); - $rootNode + $treeBuilder + ->getRootNode() ->children() ->variableNode('botdetect_captcha_path')->defaultValue($captchaLibPathDefault)->end() ->variableNode('captchaConfig')->defaultValue(null)->end() diff --git a/README.md b/README.md index f503618..c37fd5b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ -# BotDetect PHP Captcha generator integration for the Symfony framework +# ATTENTION!!! This library is not the official one, but since the official one does not seem to have support anymore I've decided to fully update it so it's working again for newer Symfony versions. I haven't put any efforts on making it compatible with Symfony 4 or older versions, but contributions are welcome. -[![Total Downloads](https://poser.pugx.org/captcha-com/symfony-captcha-bundle/downloads)](https://packagist.org/packages/captcha-com/symfony-captcha-bundle) -[![Latest Stable Version](https://poser.pugx.org/captcha-com/symfony-captcha-bundle/v/stable)](https://packagist.org/packages/captcha-com/symfony-captcha-bundle) + +# BotDetect PHP Captcha generator integration for the Symfony 5 framework (possibly on Symfony 4.4 too) + +[![Total Downloads](https://poser.pugx.org/carlos-mg89/symfony-captcha-bundle/downloads)](https://packagist.org/packages/carlos-mg89/symfony-captcha-bundle) +[![Latest Stable Version](https://poser.pugx.org/carlos-mg89/symfony-captcha-bundle/v/stable)](https://packagist.org/packages/carlos-mg89/symfony-captcha-bundle) ![BotDetect PHP CAPTCHA Library](https://captcha.com/images/help/screenshots/captcha-examples.png) @@ -26,4 +29,73 @@ ## Questions? -If you encounter bugs, implementation issues, a usage scenario you would like to discuss, or you have any questions, please contact [BotDetect CAPTCHA Support](http://captcha.com/support). \ No newline at end of file +If you encounter bugs, implementation issues, a usage scenario you would like to discuss, or you have any questions, please contact [BotDetect CAPTCHA Support](http://captcha.com/support). + +# How to install with Composer + +Simply run `composer require carlos-mg89/symfony-captcha-bundle` + +# Usage example in a Symfony 5.x project + +1. Install dependency with Composer as explained above +2. Add the following in your `config/routes.yaml`: + ``` + captcha_routing: + resource: "@CaptchaBundle/Resources/config/routing.yml" + ``` +3. Create this file `config/packages/captcha.php` with the following content (or similar): + ``` + [ + 'UserInputID' => 'captchaCode', + 'CodeLength' => CaptchaRandomization::GetRandomCodeLength(5, 6), + 'ImageWidth' => 250, + 'ImageHeight' => 50, + ], + ]; + ``` + 4. Edit your `config/services.yaml` so it autowires the controllers used in the library: + ``` + # We need to autowire the Container (or manually wire it) + services: + Captcha\Bundle\CaptchaBundle\Controller\: + resource: '../vendor/carlos-mg89/symfony-captcha-bundle/Controller' + autowire: true + ``` + 5. Edit your `FormType` or your `FormBuilderInterface` with this bit that adds the captcha along with the constraing to validate the form: + ``` + $builder->add('captchaCode', CaptchaType::class, [ + 'captchaConfig' => 'ExampleCaptcha', + 'constraints' => [ + new ValidCaptcha([ + 'message' => 'Invalid captcha, please try again', + ]), + ] + ]); + ``` + 6. Now edit your Twig template with the new `captchaCode` (`CaptchaType`): + ``` + {{ form_label(form.captchaCode) }} + {{ form_widget(form.captchaCode}) }} + ``` + 7. Finally, add the Form validation: + ``` + $contactForm = $this->createForm(ContactType::class); + $contactForm->handleRequest($request); + + if ($contactForm->isSubmitted() && $contactForm->isValid()) { + + // Do whatever you want, be it register a user, send an email, etc + + $this->addFlash('success', $translator->trans('Contact.Form.SuccessMsg')); + } elseif ($contactForm->isSubmitted() && !$contactForm->isValid()) { + throw new Exception('Invalid form'); + } + ``` + + + You have the original documentation here: https://captcha.com/doc/php/howto/symfony-captcha-bundle-integration.html#howto_display_captcha_config but it's not fully up to date nor complete. diff --git a/Resources/config/routing.yml b/Resources/config/routing.yml index f2fd26d..e93c2f5 100644 --- a/Resources/config/routing.yml +++ b/Resources/config/routing.yml @@ -1,9 +1,9 @@ captcha_handler: path: /captcha-handler - defaults: { _controller: CaptchaBundle:CaptchaHandler:index } + defaults: { _controller: Captcha\Bundle\CaptchaBundle\Controller\CaptchaHandlerController::indexAction } methods: [GET] simple_captcha_handler: path: /simple-captcha-handler - defaults: { _controller: CaptchaBundle:SimpleCaptchaHandler:index } - methods: [GET] \ No newline at end of file + defaults: { _controller: Captcha\Bundle\CaptchaBundle\Controller\SimpleCaptchaHandlerController::indexAction } + methods: [GET] diff --git a/Resources/config/services.yml b/Resources/config/services.yml index d676c87..8a08a4a 100644 --- a/Resources/config/services.yml +++ b/Resources/config/services.yml @@ -6,6 +6,9 @@ parameters: simple_captcha_type.class: Captcha\Bundle\CaptchaBundle\Form\Type\SimpleCaptchaType valid_captcha_validator.class: Captcha\Bundle\CaptchaBundle\Validator\Constraints\ValidCaptchaValidator valid_simple_captcha_validator.class: Captcha\Bundle\CaptchaBundle\Validator\Constraints\ValidSimpleCaptchaValidator + captcha: + config: + botdetect_captcha_path: '%kernel.project_dir%/vendor/' services: captcha: @@ -13,7 +16,7 @@ services: public: true arguments: - '@service_container' - + simple_captcha: class: '%botdetect_simple_captcha.class%' public: true @@ -57,4 +60,4 @@ services: arguments: - '@service_container' tags: - - { name: validator.constraint_validator, alias: valid_simple_captcha } + - { name: validator.constraint_validator, alias: valid_simple_captcha } \ No newline at end of file diff --git a/Resources/views/captcha.html.twig b/Resources/views/captcha.html.twig index b20148d..836aa7a 100644 --- a/Resources/views/captcha.html.twig +++ b/Resources/views/captcha.html.twig @@ -1,13 +1,13 @@ {% block simple_captcha_widget %} - {% spaceless %} + {% apply spaceless %} {{ captcha_html | raw }} {{ form_widget(form, { 'id': user_input_id, 'value': '' }) }} - {% endspaceless %} + {% endapply %} {% endblock %} {% block captcha_widget %} - {% spaceless %} + {% apply spaceless %} {{ captcha_html | raw }} {{ form_widget(form, { 'id': user_input_id, 'value': '' }) }} - {% endspaceless %} -{% endblock %} \ No newline at end of file + {% endapply %} +{% endblock %} diff --git a/Support/LibraryLoader.php b/Support/LibraryLoader.php index 551c171..0542023 100644 --- a/Support/LibraryLoader.php +++ b/Support/LibraryLoader.php @@ -2,9 +2,8 @@ namespace Captcha\Bundle\CaptchaBundle\Support; -use Captcha\Bundle\CaptchaBundle\Support\Path; use Captcha\Bundle\CaptchaBundle\Support\Exception\FileNotFoundException; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Psr\Container\ContainerInterface; class LibraryLoader { @@ -41,12 +40,9 @@ public function load() self::loadCaptchaConfigDefaults(); } - /** - * Load BotDetect CAPTCHA Library. - */ private function loadBotDetectLibrary() { - $libPath = Path::getCaptchaLibPath($this->container); + $libPath = Path::getDefaultLibPackageDirPath(); if (!self::isLibraryFound($libPath)) { throw new FileNotFoundException(sprintf('The BotDetect Captcha library could not be found in %s.', $libPath)); @@ -55,11 +51,6 @@ private function loadBotDetectLibrary() self::includeFile(Path::getBotDetectFilePath(), true, $libPath); } - /** - * Load the captcha configuration defaults. - * - * @param SessionInterface $session - */ private function loadCaptchaConfigDefaults() { self::includeFile(Path::getCaptchaConfigDefaultsFilePath(), true, $this->session); diff --git a/Support/Path.php b/Support/Path.php index f7a1754..c5e39e0 100644 --- a/Support/Path.php +++ b/Support/Path.php @@ -3,7 +3,7 @@ namespace Captcha\Bundle\CaptchaBundle\Support; use Symfony\Component\HttpKernel\Kernel; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Psr\Container\ContainerInterface; final class Path { @@ -31,7 +31,7 @@ public static function getCaptchaLibPath(ContainerInterface $container) */ public static function getDefaultLibPackageDirPath() { - $libPath1 = __DIR__ . '/../../captcha/botdetect-captcha-lib'; + $libPath1 = __DIR__ . '/../../../captcha-com/captcha/botdetect-captcha-lib'; $libPath2 = __DIR__ . '/../../captcha/lib'; if (is_dir($libPath1)) { @@ -50,9 +50,9 @@ public static function getDefaultLibPackageDirPath() * * @return string */ - public static function getPublicDirPathInLibrary(ContainerInterface $container) + public static function getPublicDirPathInLibrary() { - return self::getCaptchaLibPath($container) . '/botdetect/public/'; + return self::getDefaultLibPackageDirPath() . '/botdetect/public/'; } /** diff --git a/Support/SimpleLibraryLoader.php b/Support/SimpleLibraryLoader.php index c87a06d..0fb8032 100644 --- a/Support/SimpleLibraryLoader.php +++ b/Support/SimpleLibraryLoader.php @@ -2,9 +2,8 @@ namespace Captcha\Bundle\CaptchaBundle\Support; -use Captcha\Bundle\CaptchaBundle\Support\Path; use Captcha\Bundle\CaptchaBundle\Support\Exception\FileNotFoundException; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Psr\Container\ContainerInterface; class SimpleLibraryLoader { @@ -28,13 +27,9 @@ public function __construct(ContainerInterface $container) */ public function load() { - // load bd php library self::loadBotDetectLibrary(); } - /** - * Load BotDetect CAPTCHA Library. - */ private function loadBotDetectLibrary() { $libPath = Path::getCaptchaLibPath($this->container); diff --git a/composer.json b/composer.json index 8dc08fd..61e21fe 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "captcha-com/symfony-captcha-bundle", + "name": "carlos-mg89/symfony-captcha-bundle", "type": "symfony-bundle", "description": "Symfony Captcha Bundle -- BotDetect PHP CAPTCHA generator integration for the Symfony framework.", "keywords": ["symfony captcha bundle", "symfony captcha", "captcha bundle", "captcha", "captcha generator", "botdetect"], @@ -12,7 +12,7 @@ } ], "support": { - "email": "botdetect.support@captcha.com", + "email": "carlosmartinezgadea@gmail.com", "issues": "https://captcha.com/support", "forum": "https://captcha.com/support", "source": "https://captcha.com/doc/php/symfony-captcha-bundle-quickstart.html"