2016-01-19 15:30:55 +01:00
|
|
|
<?php
|
2023-07-18 16:59:11 +02:00
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
namespace OxidProfessionalServices\Captcha\Application\Core;
|
|
|
|
|
|
|
|
use OxidEsales\Eshop\Core\Registry;
|
|
|
|
use OxidEsales\Eshop\Core\UtilsObject;
|
|
|
|
use OxidProfessionalServices\Captcha\Application\Shared\Connection;
|
|
|
|
use OxidProfessionalServices\Captcha\Application\Shared\Options;
|
|
|
|
|
|
|
|
class Captcha
|
2016-01-19 15:30:55 +01:00
|
|
|
{
|
2023-07-18 16:59:11 +02:00
|
|
|
use Connection;
|
|
|
|
use Options;
|
|
|
|
public const ENCRYPT_KEY = 'fq45QS09_fqyx09239QQ';
|
|
|
|
|
2016-01-19 15:30:55 +01:00
|
|
|
/**
|
2023-07-18 16:59:11 +02:00
|
|
|
* CAPTCHA length.
|
2016-01-19 15:30:55 +01:00
|
|
|
*
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected $macLength = 5;
|
|
|
|
|
|
|
|
/**
|
2023-07-18 16:59:11 +02:00
|
|
|
* Captcha text.
|
2016-01-19 15:30:55 +01:00
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
2023-07-18 16:59:11 +02:00
|
|
|
protected $text;
|
2016-01-19 15:30:55 +01:00
|
|
|
|
|
|
|
/**
|
2023-07-18 16:59:11 +02:00
|
|
|
* Possible CAPTCHA chars, no ambiguities.
|
2016-01-19 15:30:55 +01:00
|
|
|
*
|
|
|
|
* @var string
|
|
|
|
*/
|
|
|
|
protected $macChars = 'abcdefghijkmnpqrstuvwxyz23456789';
|
|
|
|
|
|
|
|
/**
|
2023-07-18 16:59:11 +02:00
|
|
|
* Captcha timeout 60 * 5 = 5 minutes.
|
2016-01-19 15:30:55 +01:00
|
|
|
*
|
|
|
|
* @var int
|
|
|
|
*/
|
|
|
|
protected $timeout = 300;
|
|
|
|
|
2023-07-18 16:59:11 +02:00
|
|
|
public static function getInstance(): static
|
|
|
|
{
|
|
|
|
return oxNew(static::class);
|
|
|
|
}
|
|
|
|
|
2016-01-19 15:30:55 +01:00
|
|
|
/**
|
2023-07-18 16:59:11 +02:00
|
|
|
* Returns text.
|
2016-01-19 15:30:55 +01:00
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getText()
|
|
|
|
{
|
|
|
|
if (!$this->text) {
|
|
|
|
$this->text = '';
|
2023-07-18 16:59:11 +02:00
|
|
|
|
|
|
|
for ($i = 0; $i < $this->macLength; ++$i) {
|
2021-10-22 14:11:29 +02:00
|
|
|
$this->text .= strtolower($this->macChars[rand(0, strlen($this->macChars) - 1)]);
|
2016-01-19 15:30:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->text;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-07-18 16:59:11 +02:00
|
|
|
* Returns given string captcha hash.
|
2016-01-19 15:30:55 +01:00
|
|
|
*
|
|
|
|
* @param string $text string to hash
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getTextHash($text)
|
|
|
|
{
|
|
|
|
if (!$text) {
|
|
|
|
$text = $this->getText();
|
|
|
|
}
|
|
|
|
$text = strtolower($text);
|
|
|
|
|
|
|
|
return md5('ox' . $text);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-07-18 16:59:11 +02:00
|
|
|
* Check if captcha is passed.
|
2016-01-19 15:30:55 +01:00
|
|
|
*
|
2023-07-18 16:59:11 +02:00
|
|
|
* @param mixed $displayError
|
|
|
|
*
|
|
|
|
* @return bool
|
2016-01-19 15:30:55 +01:00
|
|
|
*/
|
2023-07-18 16:59:11 +02:00
|
|
|
public function passCaptcha($displayError = true)
|
2016-01-19 15:30:55 +01:00
|
|
|
{
|
2023-07-18 16:59:11 +02:00
|
|
|
$return = true;
|
2017-11-10 09:26:32 +01:00
|
|
|
|
2023-07-18 16:59:11 +02:00
|
|
|
// spam spider prevention
|
|
|
|
$mac = Registry::getConfig()->getRequestParameter('c_mac');
|
|
|
|
$macHash = Registry::getConfig()->getRequestParameter('c_mach');
|
2017-11-10 09:26:32 +01:00
|
|
|
|
2023-07-18 16:59:11 +02:00
|
|
|
if (!$this->pass($mac, $macHash)) {
|
|
|
|
$return = false;
|
|
|
|
}
|
2016-01-19 15:30:55 +01:00
|
|
|
|
2023-07-18 16:59:11 +02:00
|
|
|
if (!$return && $displayError) {
|
|
|
|
// even if there is no exception, use this as a default display method
|
|
|
|
Registry::getUtilsView()->addErrorToDisplay('MESSAGE_WRONG_VERIFICATION_CODE');
|
|
|
|
}
|
|
|
|
|
|
|
|
return $return;
|
2016-01-19 15:30:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-07-18 16:59:11 +02:00
|
|
|
* Checks if image could be generated.
|
2016-01-19 15:30:55 +01:00
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function isImageVisible()
|
|
|
|
{
|
2023-07-18 16:59:11 +02:00
|
|
|
return (function_exists('imagecreatetruecolor') || function_exists('imagecreate')) && Registry::getConfig()->getConfigParam('iUseGDVersion') > 1;
|
2016-01-19 15:30:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-07-18 16:59:11 +02:00
|
|
|
* Returns url to CAPTCHA image generator.
|
2016-01-19 15:30:55 +01:00
|
|
|
*
|
2023-07-18 16:59:11 +02:00
|
|
|
* @return string
|
2016-01-19 15:30:55 +01:00
|
|
|
*/
|
2023-07-18 16:59:11 +02:00
|
|
|
public function getImageUrl()
|
2016-01-19 15:30:55 +01:00
|
|
|
{
|
2023-07-18 16:59:11 +02:00
|
|
|
$config = Registry::getConfig();
|
|
|
|
$key = $this->getOeCaptchaKey();
|
|
|
|
$encryptor = new \OxidEsales\Eshop\Core\Encryptor();
|
2016-01-19 15:30:55 +01:00
|
|
|
|
2023-07-18 16:59:11 +02:00
|
|
|
return $config->getCurrentShopUrl() . sprintf('?cl=ith_basic_captcha_generator&e_mac=%s&shp=%d', $encryptor->encrypt($this->getText(), $key), $config->getShopId());
|
|
|
|
}
|
2016-01-19 15:30:55 +01:00
|
|
|
|
2023-07-18 16:59:11 +02:00
|
|
|
/**
|
|
|
|
* Returns text hash.
|
|
|
|
*
|
|
|
|
* @param string $text User supplie text
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getHash($text = null)
|
|
|
|
{
|
|
|
|
// inserting captcha record
|
|
|
|
$time = time() + $this->timeout;
|
|
|
|
$textHash = $this->getTextHash($text);
|
2016-01-19 15:30:55 +01:00
|
|
|
|
2023-07-18 16:59:11 +02:00
|
|
|
// if session is started - storing captcha info here
|
|
|
|
$session = Registry::getSession();
|
|
|
|
if ($session->isSessionStarted()) {
|
|
|
|
$hash = UtilsObject::getInstance()->generateUID();
|
|
|
|
$hashArray = $session->getVariable('captchaHashes');
|
|
|
|
$hashArray[$hash] = [$textHash => $time];
|
|
|
|
$session->setVariable('captchaHashes', $hashArray);
|
|
|
|
} else {
|
|
|
|
$q = $this->getQueryBuilder();
|
|
|
|
$q->insert('oecaptcha')
|
|
|
|
->values(
|
|
|
|
[
|
|
|
|
'oxhash' => '?',
|
|
|
|
'oxtime' => '?',
|
|
|
|
]
|
|
|
|
)->setParameter(0, $textHash)->setParameter(1, $time);
|
|
|
|
$q->execute();
|
|
|
|
$hash = $q->getConnection()->lastInsertId();
|
2016-01-19 15:30:55 +01:00
|
|
|
}
|
|
|
|
|
2023-07-18 16:59:11 +02:00
|
|
|
return $hash;
|
2016-01-19 15:30:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-07-18 16:59:11 +02:00
|
|
|
* Checks for DB captcha hash validity.
|
2016-01-19 15:30:55 +01:00
|
|
|
*
|
2023-07-18 16:59:11 +02:00
|
|
|
* @param int $macHash hash key
|
|
|
|
* @param string $hash captcha hash
|
|
|
|
* @param int $time check time
|
2016-01-19 15:30:55 +01:00
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
2023-07-18 16:59:11 +02:00
|
|
|
protected function passFromDb($macHash, $hash, $time)
|
2016-01-19 15:30:55 +01:00
|
|
|
{
|
2023-07-18 16:59:11 +02:00
|
|
|
$q = $this->getQueryBuilder();
|
|
|
|
$q->select('1')
|
|
|
|
->from('oecaptcha')
|
|
|
|
->where('oxid = :macHash')
|
|
|
|
->andWhere('oxhash = :hash')
|
|
|
|
->setParameter('macHash', $macHash)
|
|
|
|
->setParameter('hash', $hash);
|
|
|
|
$pass = (bool) $q->execute()->fetchOne();
|
|
|
|
if ($pass) {
|
|
|
|
// cleanup
|
|
|
|
$q = $this->getQueryBuilder()
|
|
|
|
->delete('oecaptcha')
|
|
|
|
->where('oxid = :macHash')
|
|
|
|
->andWhere('oxhash = :hash')
|
|
|
|
->setParameter('macHash', $macHash)
|
|
|
|
->setParameter('hash', $hash);
|
|
|
|
$q->execute();
|
2016-01-19 15:30:55 +01:00
|
|
|
}
|
|
|
|
|
2023-07-18 16:59:11 +02:00
|
|
|
// garbage cleanup
|
|
|
|
$q = $this->getQueryBuilder()
|
|
|
|
->delete('oecaptcha')
|
|
|
|
->where('oxtime < :time')
|
|
|
|
->setParameter('time', $time);
|
|
|
|
$q->execute();
|
|
|
|
|
|
|
|
return $pass;
|
2016-01-19 15:30:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-07-18 16:59:11 +02:00
|
|
|
* Checks for session captcha hash validity.
|
2016-01-19 15:30:55 +01:00
|
|
|
*
|
|
|
|
* @param string $macHash hash key
|
|
|
|
* @param string $hash captcha hash
|
|
|
|
* @param int $time check time
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
protected function passFromSession($macHash, $hash, $time)
|
|
|
|
{
|
2023-07-18 16:59:11 +02:00
|
|
|
$pass = null;
|
|
|
|
$session = Registry::getSession();
|
2016-01-19 15:30:55 +01:00
|
|
|
|
2023-07-18 16:59:11 +02:00
|
|
|
if ($hashArray = $session->getVariable('captchaHashes')) {
|
2016-01-19 15:30:55 +01:00
|
|
|
$pass = (isset($hashArray[$macHash][$hash]) && $hashArray[$macHash][$hash] >= $time) ? true : false;
|
|
|
|
unset($hashArray[$macHash]);
|
|
|
|
if (!empty($hashArray)) {
|
|
|
|
$session->setVariable('captchaHashes', $hashArray);
|
|
|
|
} else {
|
|
|
|
$session->deleteVariable('captchaHashes');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $pass;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2023-07-18 16:59:11 +02:00
|
|
|
* Verifies captcha input vs supplied hash. Returns true on success.
|
2016-01-19 15:30:55 +01:00
|
|
|
*
|
2023-07-18 16:59:11 +02:00
|
|
|
* @param string $mac User supplied text
|
|
|
|
* @param string $macHash Generated hash
|
2016-01-19 15:30:55 +01:00
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
2023-07-18 16:59:11 +02:00
|
|
|
protected function pass($mac, $macHash)
|
2016-01-19 15:30:55 +01:00
|
|
|
{
|
2023-07-18 16:59:11 +02:00
|
|
|
$time = time();
|
|
|
|
$hash = $this->getTextHash($mac);
|
|
|
|
$pass = $this->passFromSession($macHash, $hash, $time);
|
2016-01-19 15:30:55 +01:00
|
|
|
|
2023-07-18 16:59:11 +02:00
|
|
|
// if captcha info was NOT stored in session
|
|
|
|
if (null === $pass) {
|
|
|
|
$pass = $this->passFromDb((int) $macHash, $hash, $time);
|
2016-01-19 15:30:55 +01:00
|
|
|
}
|
|
|
|
|
2023-07-18 16:59:11 +02:00
|
|
|
return (bool) $pass;
|
2016-01-19 15:30:55 +01:00
|
|
|
}
|
|
|
|
}
|