On March 26, 2019 we launched new MODX Forums. Please join us at the new MODX Community Forums.
Subscribe: RSS
  • Quote from: BobRay at Jun 21, 2016, 12:38 PM
    This looks like a pretty good explanation of the necessary changes to migrate from V1 to V2: https://blog.sendsafely.com/migrating-to-googles-nocaptcha-recaptcha . It might not require changes to the code of MODX or any MODX extras.

    Thanks Bob - not sure how this relates to getting it to work in Quip though?
    • Would the code in file "recaotcha.class.php" in folder "core/components/quip/model/recaptcha" need updating to use the v2 format?

      <?php
      /**
       * reCaptcha Integration
       *
       * Copyright 2009-2010 by Shaun McCormick <shaun@modx.com>
       *
       * reCaptcha Integration is free software; you can redistribute it and/or modify
       * it under the terms of the GNU General Public License as published by the Free
       * Software Foundation; either version 2 of the License, or (at your option) any
       * later version.
       *
       * reCaptcha Integration is distributed in the hope that it will be useful, but
       * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
       * details.
       *
       * You should have received a copy of the GNU General Public License along with
       * reCaptcha Integration; if not, write to the Free Software Foundation, Inc.,
       * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
       *
       * @package recaptcha
       */
      /**
       * reCaptcha modX service class.
       *
       * Based off of recaptchalib.php by Mike Crawford and Ben Maurer. Changes include converting to OOP and making a class.
       *
       * @package recaptcha
       */
      class reCaptcha {
          const OPT_API_SERVER = 'api_server';
          const OPT_API_SECURE_SERVER = 'api_secure_server';
          const OPT_API_VERIFY_SERVER = 'api_verify_server';
          const OPT_PRIVATE_KEY = 'privateKey';
          const OPT_PUBLIC_KEY = 'publicKey';
          const OPT_USE_SSL = 'use_ssl';
      
          function __construct(modX &$modx,array $config = array()) {
              $this->modx =& $modx;
              $this->modx->lexicon->load('quip:recaptcha');
              $this->config = array_merge(array(
                  reCaptcha::OPT_PRIVATE_KEY => $this->modx->getOption('recaptcha.private_key',$config,''),
                  reCaptcha::OPT_PUBLIC_KEY => $this->modx->getOption('recaptcha.public_key',$config,''),
                  reCaptcha::OPT_USE_SSL => $this->modx->getOption('recaptcha.use_ssl',$config,false),
                  reCaptcha::OPT_API_SERVER => 'http://www.google.com/recaptcha/api/',
                  reCaptcha::OPT_API_SECURE_SERVER => 'https://www.google.com/recaptcha/api/',
                  reCaptcha::OPT_API_VERIFY_SERVER => 'api-verify.recaptcha.net',
              ),$config);
          }
      
          /**
           * Encodes the given data into a query string format
           * @param $data - array of string elements to be encoded
           * @return string - encoded request
           */
          protected function qsencode($data) {
              $req = '';
              foreach ($data as $key => $value) {
                  $req .= $key . '=' . urlencode( stripslashes($value) ) . '&';
              }
      
              // Cut the last '&'
              $req=substr($req,0,strlen($req)-1);
              return $req;
          }
      
          /**
           * Submits an HTTP POST to a reCAPTCHA server
           * @param string $host
           * @param string $path
           * @param array $data
           * @param int port
           * @return array response
           */
          protected function httpPost($host, $path, array $data = array(), $port = 80) {
              $data['privatekey'] = $this->config[reCaptcha::OPT_PRIVATE_KEY];
              $req = $this->qsencode($data);
      
              $http_request  = "POST $path HTTP/1.0\r\n";
              $http_request .= "Host: $host\r\n";
              $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n";
              $http_request .= "Content-Length: " . strlen($req) . "\r\n";
              $http_request .= "User-Agent: reCAPTCHA/PHP\r\n";
              $http_request .= "\r\n";
              $http_request .= $req;
      
              $response = '';
              if(false == ($fs = @fsockopen($host, $port, $errno, $errstr, 10))) {
                  return 'Could not open socket';
              }
      
              fwrite($fs, $http_request);
              while (!feof($fs)) {
                  $response .= fgets($fs, 1160); // One TCP-IP packet
              }
              fclose($fs);
              $response = explode("\r\n\r\n", $response, 2);
      
              return $response;
          }
          
          /**
           * Gets the challenge HTML (javascript and non-javascript version).
           * This is called from the browser, and the resulting reCAPTCHA HTML widget
           * is embedded within the HTML form it was called from.
           * @param string $theme The theme to use
           * @param string $error The error given by reCAPTCHA (optional, default is null)
           * @param boolean $use_ssl Should the request be made over ssl? (optional, default is false)
      
           * @return string - The HTML to be embedded in the user's form.
           */
          public function getHtml($theme = 'clean',$error = null) {
              if (empty($this->config[reCaptcha::OPT_PUBLIC_KEY])) {
                  return $this->error($this->modx->lexicon('recaptcha.no_api_key'));
              }
      
              /* use ssl or not */
              $server = !empty($this->config[reCaptcha::OPT_USE_SSL]) ? $this->config[reCaptcha::OPT_API_SECURE_SERVER] : $this->config[reCaptcha::OPT_API_SERVER];
      
              $errorpart = '';
              if ($error) {
                 $errorpart = "&error=" . $error;
              }
              $opt = array(
                  'theme' => $theme,
                  'lang' => $this->modx->getOption('cultureKey',null,'en'),
              );
              return '<script type="text/javascript">var RecaptchaOptions = '.$this->modx->toJSON($opt).';</script><script type="text/javascript" src="'. $server . 'challenge?k=' . $this->config[reCaptcha::OPT_PUBLIC_KEY] . $errorpart . '"></script>
              <noscript>
                      <iframe src="'. $server . 'noscript?k=' . $this->config[reCaptcha::OPT_PUBLIC_KEY] . $errorpart . '" height="300" width="500" frameborder="0"></iframe><br/>
                      <textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
                      <input type="hidden" name="recaptcha_response_field" value="manual_challenge"/>
              </noscript>';
          }
      
          protected function error($message = '') {
              $response = new reCaptchaResponse();
              $response->is_valid = false;
              $response->error = $message;
              return $message;
          }
      
          /**
            * Calls an HTTP POST function to verify if the user's guess was correct
            * @param string $remoteip
            * @param string $challenge
            * @param string $response
            * @param array $extra_params an array of extra variables to post to the server
            * @return ReCaptchaResponse
            */
          public function checkAnswer ($remoteIp, $challenge, $responseField, $extraParams = array()) {
              if (empty($this->config[reCaptcha::OPT_PRIVATE_KEY])) {
                  return $this->error($this->modx->lexicon('recaptcha.no_api_key'));
              }
      
              if (empty($remoteIp)) {
                  return $this->error($this->modx->lexicon('recaptcha.no_remote_ip'));
              }
      
              //discard spam submissions
              if (empty($challenge) || empty($responseField)) {
                  return $this->error($this->modx->lexicon('recaptcha.empty_answer'));
              }
      
              $verifyServer = $this->config[reCaptcha::OPT_API_VERIFY_SERVER];
              $response = $this->httpPost($verifyServer,"/verify",array (
                  'remoteip' => $remoteIp,
                  'challenge' => $challenge,
                  'response' => $responseField,
              ) + $extraParams);
      
              $answers = explode("\n",$response[1]);
              $response = new reCaptchaResponse();
      
              if (trim($answers[0]) == 'true') {
                  $response->is_valid = true;
              } else {
                  $response->is_valid = false;
                  $response->error = $answers [1];
              }
              return $response;
          }
      
          /**
           * gets a URL where the user can sign up for reCAPTCHA. If your application
           * has a configuration page where you enter a key, you should provide a link
           * using this function.
           * @param string $domain The domain where the page is hosted
           * @param string $appname The name of your application
           */
          public function getSignupUrl ($domain = null, $appname = null) {
              return "http://recaptcha.net/api/getkey?" .  $this->qsencode(array ('domain' => $domain, 'app' => $appname));
          }
      
          protected function aesPad($val) {
              $block_size = 16;
              $numpad = $block_size - (strlen ($val) % $block_size);
              return str_pad($val, strlen ($val) + $numpad, chr($numpad));
          }
      
          /* Mailhide related code */
          protected function aesEncrypt($val,$ky) {
              if (!function_exists("mcrypt_encrypt")) {
                  return $this->error($this->modx->lexicon('recaptcha.mailhide_no_mcrypt'));
              }
              $mode=MCRYPT_MODE_CBC;
              $enc=MCRYPT_RIJNDAEL_128;
              $val= $this->aesPad($val);
              return mcrypt_encrypt($enc, $ky, $val, $mode, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
          }
      
      
          protected function mailhideUrlbase64 ($x) {
              return strtr(base64_encode ($x), '+/', '-_');
          }
      
          /* gets the reCAPTCHA Mailhide url for a given email, public key and private key */
          public function mailhideUrl($email) {
              if (empty($this->config[reCaptcha::OPT_PUBLIC_KEY]) || empty($this->config[reCaptcha::OPT_PRIVATE_KEY])) {
                  return $this->error($this->modx->lexicon('recaptcha.mailhide_no_api_key'));
              }
      
              $ky = pack('H*',$this->config[reCaptcha::OPT_PRIVATE_KEY]);
              $cryptmail = $this->aesEncrypt($email, $ky);
              return 'http://mailhide.recaptcha.net/d?k='
                  . $this->config[reCaptcha::OPT_PUBLIC_KEY]
                  . '&c=' . $this->mailhideUrlbase64($cryptmail);
          }
      
          /**
           * gets the parts of the email to expose to the user.
           * eg, given johndoe@example,com return ["john", "example.com"].
           * the email is then displayed as john...@example.com
           */
          public function mailhideEmailParts ($email) {
              $arr = preg_split("/@/", $email);
      
              if (strlen($arr[0]) <= 4) {
                  $arr[0] = substr($arr[0], 0, 1);
              } else if (strlen ($arr[0]) <= 6) {
                  $arr[0] = substr($arr[0], 0, 3);
              } else {
                  $arr[0] = substr($arr[0], 0, 4);
              }
              return $arr;
          }
      
          /**
           * Gets html to display an email address given a public an private key.
           * to get a key, go to:
           *
           * http://mailhide.recaptcha.net/apikey
           */
          public function mailhideHtml($email) {
              $emailparts = $this->mailhideEmailParts($email);
              $url = $this->mailhideUrl($email);
      
              return htmlentities($emailparts[0]) . "<a href='" . htmlentities ($url) .
                  "' onclick=\"window.open('" . htmlentities ($url) . "', '', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=500,height=300'); return false;\" title=\"Reveal this e-mail address\">...</a>@" . htmlentities ($emailparts [1]);
          }
      
      
      }
      
      /**
       * A reCaptchaResponse is returned from reCaptcha::check_answer()
       */
      class reCaptchaResponse {
          public $is_valid;
          public $error;
      }
      
      
      • Yes, it would have to change some. The changes described in the link I gave you above seem fairly straightforward
          Did I help you? Buy me a beer
          Get my Book: MODX:The Official Guide
          MODX info for everyone: http://bobsguides.com/modx.html
          My MODX Extras
          Bob's Guides is now hosted at A2 MODX Hosting
        • Quote from: BobRay at Feb 26, 2018, 06:34 PM
          Yes, it would have to change some. The changes described in the link I gave you above seem fairly straightforward
          For someone with more PHP skills than me perhaps.. Any PHP whizkids fancy a challenge to adapt this to get it to work with the v2 recaptcha tool?
          • Does anyone fancy updating the "core/components/quip/model/recaptcha/recaptcha.class.php" to work for v2 recaptcha?
            • Hi Bob - I'm hoping to get the recaptchaV2 addon (https://modx.com/extras/package/recaptchav2) to work and validate the recaptcha is completed in Quip - but even when the recaptcha is succesfully checked - the validation fails and says it is not... any ideas on a fix?
              • I don't really know how Quip uses Recaptcha. I'll try to find time to take a look, but IIRC, the Quip code is tough to follow. AFAIK, Quip hasn't been updated for over 5 years and is no longer supported or under development..

                  Did I help you? Buy me a beer
                  Get my Book: MODX:The Official Guide
                  MODX info for everyone: http://bobsguides.com/modx.html
                  My MODX Extras
                  Bob's Guides is now hosted at A2 MODX Hosting
                • Quote from: BobRay at Feb 28, 2018, 11:57 PM
                  I don't really know how Quip uses Recaptcha. I'll try to find time to take a look, but IIRC, the Quip code is tough to follow. AFAIK, Quip hasn't been updated for over 5 years and is no longer supported or under development..


                  Many thanks Bob - yeh, shame it seems to have been left to die - was a good tool to give MODx a blogging capacity - its a shame there are not more MODx Open Source enthusiasts out there to help keep things alive!