type * * @var object */ public $captcha = ''; /** * Class constructor: decides whether to create an instance of * Laminas\Captcha\Image or Laminas\Captcha\ReCaptcha or Captcha_Question * * @param string $type recaptcha|questions|default|dumb */ public function __construct($type = '') { global $prefs; if (empty($type)) { if ($prefs['recaptcha_enabled'] == 'y' && ! empty($prefs['recaptcha_privkey']) && ! empty($prefs['recaptcha_pubkey'])) { if ($prefs['recaptcha_version'] == '2') { $type = 'recaptcha20'; } elseif ($prefs['recaptcha_version'] == '3') { $type = 'recaptcha30'; } else { $type = 'recaptcha'; } } elseif ($prefs['captcha_questions_active'] == 'y' && ! empty($prefs['captcha_questions'])) { $type = 'questions'; } elseif (extension_loaded('gd') && function_exists('imagepng') && function_exists('imageftbbox')) { $type = 'default'; } else { $type = 'dumb'; } } if ($type === 'recaptcha') { $this->captcha = new Laminas\Captcha\ReCaptcha( [ 'private_key' => $prefs['recaptcha_privkey'], 'public_key' => $prefs['recaptcha_pubkey'], ] ); $httpClient = TikiLib::lib('tiki')->get_http_client(); $this->captcha->getService()->setHttpClient($httpClient); $this->captcha->getService()->setOption('theme', isset($prefs['recaptcha_theme']) ? $prefs['recaptcha_theme'] : 'clean'); $this->captcha->setOption('ssl', true); $this->type = $type; $this->recaptchaCustomTranslations(); } elseif (in_array($type, ['recaptcha20', 'recaptcha30'])) { $params = [ 'privkey' => $prefs['recaptcha_privkey'], 'pubkey' => $prefs['recaptcha_pubkey'], 'theme' => isset($prefs['recaptcha_theme']) ? $prefs['recaptcha_theme'] : 'clean', ]; if ($type === 'recaptcha20') { include_once('lib/captcha/Captcha_ReCaptcha20.php'); $this->captcha = new Captcha_ReCaptcha20($params); } else { include_once('lib/captcha/Captcha_ReCaptcha30.php'); $this->captcha = new Captcha_ReCaptcha30($params); } $httpClient = TikiLib::lib('tiki')->get_http_client(); $this->captcha->getService()->setHttpClient($httpClient); $this->captcha->setOption('ssl', true); $this->type = $type; $this->recaptchaCustomTranslations(); } elseif ($type === 'default') { $this->captcha = new Laminas\Captcha\Image( [ 'wordLen' => $prefs['captcha_wordLen'], 'timeout' => 600, 'font' => __DIR__ . '/DejaVuSansMono.ttf', 'imgdir' => 'temp/public/', 'suffix' => '.captcha.png', 'width' => $prefs['captcha_width'], 'dotNoiseLevel' => $prefs['captcha_noise'], ] ); $this->type = 'default'; } elseif ($type === 'questions') { $this->type = 'questions'; $questions = []; $lines = explode("\n", $prefs['captcha_questions']); foreach ($lines as $line) { $line = explode(':', $line, 2); if (count($line) === 2) { $questions[] = [trim($line[0]), trim($line[1])]; } } include_once('lib/captcha/Captcha_Questions.php'); $this->captcha = new Captcha_Questions($questions); } else { // implied $type==='dumb' $this->captcha = new Laminas\Captcha\Dumb(); $this->captcha->setWordlen($prefs['captcha_wordLen']); $this->captcha->setLabel(tra('Please type this word backwards')); $this->type = 'dumb'; } $this->setErrorMessages(); } /** * Create the default captcha * * @return string */ public function generate() { $key = ''; try { $key = $this->captcha->generate(); if ($this->type == 'default' || $this->type == 'questions') { // the following needed to keep session active for ajax checking $session = $this->captcha->getSession(); $session->setExpirationHops(2, null, true); $this->captcha->setSession($session); $this->captcha->setKeepSession(false); } } catch (Exception $e) { Feedback::error($e->getMessage()); } return $key; } /** Return captcha ID * * @return string captcha ID */ public function getId() { return $this->captcha->getId(); } /** * HTML code for the captcha * * @return string */ public function render() { $access = TikiLib::lib('access'); if ($access->is_xml_http_request()) { if (in_array($this->type, ['recaptcha20', 'recaptcha30'])) { return $this->captcha->renderAjax(); } elseif ($this->type == 'recaptcha') { $params = json_encode($this->captcha->getService()->getOptions()); $id = 1; TikiLib::lib('header')->add_js(' Recaptcha.create("' . $this->captcha->getPubKey() . '", "captcha' . $id . '",' . $params . ' ); ', 100); return '
'; } else { return $this->captcha->render(); } } else { if (in_array($this->type, ['recaptcha20', 'recaptcha30'])) { return $this->captcha->render(); } elseif ($this->captcha instanceof Laminas\Captcha\ReCaptcha) { return $this->captcha->getService()->getHtml(); } elseif ($this->captcha instanceof Laminas\Captcha\Dumb) { return $this->captcha->getLabel() . ': ' . strrev($this->captcha->getWord()) . ''; } return $this->captcha->render(); } } /** * Validate user input for the captcha * * @param array $input * @return bool true or false */ public function validate($input = null) { if (is_null($input)) { $input = $_REQUEST; } if (in_array($this->type, ['recaptcha', 'recaptcha20', 'recaptcha30'])) { // Temporary workaround of zend/http client uses arg_separator.output for making POST request body // which fails with Google recaptcha services if used with '&' value // should be fixed in zend/http (pull request submitted) // or remove ini_get('arg_separator.output', '&') we have in tiki code tiki-setup_base.php:31 $oldVal = ini_get('arg_separator.output'); ini_set('arg_separator.output', '&'); $result = $this->captcha->isValid($input); ini_set('arg_separator.output', $oldVal); return $result; } else { if (isset($input['captcha'])) { $captchaInput = $input['captcha']; } else { global $jitPost; $captchaInput = $jitPost->captcha->asArray(); } return $this->captcha->isValid($captchaInput); } } /** * Return the full path to the captcha image when using default captcha * * @return string full path to default captcha image */ public function getPath() { try { return $this->captcha->getImgDir() . $this->captcha->getId() . $this->captcha->getSuffix(); } catch (Exception $e) { Feedback::error($e->getMessage()); } } /** * Translate Laminas\Captcha\Image, Laminas\Captcha\Dumb and Laminas\Captcha\ReCaptcha * default error messages * * @return void */ public function setErrorMessages() { $errors = [ 'missingValue' => tra('Empty CAPTCHA value'), 'badCaptcha' => tra('You have mistyped the anti-bot verification code. Please try again.') ]; if (in_array($this->type, ['recaptcha', 'recaptcha20', 'recaptcha30'])) { $errors['errCaptcha'] = tra('Failed to validate CAPTCHA'); } else { $errors['missingID'] = tra('CAPTCHA ID field is missing'); } $this->captcha->setMessages($errors); } /** * Convert the errors array into a string and return it * * @return string error messages */ public function getErrors() { return implode('