PageRenderTime 4ms CodeModel.GetById 36ms app.highlight 14ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/Lampcms/Captcha/Captcha.php

http://github.com/snytkine/LampCMS
PHP | 851 lines | 351 code | 133 blank | 367 comment | 64 complexity | 780a8e5f14edeae57b417efff29d6b19 MD5 | raw file
  1<?php
  2
  3
  4
  5/**
  6 * PHP-Class hn_captcha_X1 Version 1.2.1, released 30-Apr-2009
  7 *
  8 * is an extension for PHP-Class hn_captcha, Version for PHP 5 !
  9 *
 10 * It adds a garbage-collector. (Useful, if you cannot use cronjobs.)
 11 *
 12 *
 13 * Author: Horst Nogajski, coding@nogajski.de
 14 *
 15 * $Id: hn_captcha.class.x1.php5,v 1.4.2.2 2009/04/30 14:30:06 horst Exp $
 16 *
 17 * Download: http://hn273.users.phpclasses.org/browse/package/1569.html
 18 *
 19 * License: GNU LGPL (http://www.opensource.org/licenses/lgpl-license.html)
 20 *
 21 * This library is free software; you can redistribute it and/or
 22 * modify it under the terms of the GNU Lesser General Public
 23 * License as published by the Free Software Foundation; either
 24 * version 2.1 of the License, or (at your option) any later version.
 25 *
 26 * This library is distributed in the hope that it will be useful,
 27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 29 * Lesser General Public License for more details.
 30 *
 31 * You should have received a copy of the GNU Lesser General Public
 32 * License along with this library; if not, write to the Free Software
 33 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 34 *
 35 *
 36 * This class generates a picture to use in forms that perform CAPTCHA test
 37 * (Completely Automated Public Turing to tell Computers from Humans Apart).
 38 * After the test form is submitted a key entered by the user in a text field
 39 * is compared by the class to determine whether it matches the text in the picture.
 40 *
 41 * The class is a fork of the original released at www.phpclasses.org
 42 * by Julien Pachet with the name ocr_captcha.
 43 *
 44 * The following enhancements were added:
 45 *
 46 * - Support to make it work with GD library before version 2
 47 * - Hacking prevention
 48 * - Optional use of Web safe colors
 49 * - Limit the number of users attempts
 50 * - Display an optional refresh link to generate a new picture with a different key
 51 *   without counting to the user attempts limit verification
 52 * - Support the use of multiple random TrueType fonts
 53 * - Control the output image by only three parameters: number of text characters
 54 *   and minimum and maximum size preserving the size proportion
 55 * - Preserve all request parameters passed to the page via the GET method,
 56 *   so the CAPTCHA test can be added to existing scripts with minimal changes
 57 * - Added a debug option for testing the current configuration
 58 */
 59
 60namespace Lampcms\Captcha;
 61
 62use \Lampcms\DevException;
 63
 64/**
 65 * All the configuration settings are passed to the class in an array when the object instance is initialized.
 66 *
 67 * The class only needs two function calls to be used: display_form() and validate_submit().
 68 *
 69 *
 70 * Class that generate a captcha-image with text and a form to fill in this text
 71 *
 72 * @author  Horst Nogajski, (mail: horst@nogajski.de)
 73 * @version 1.3
 74 *
 75 */
 76class Captcha
 77{
 78
 79    ////////////////////////////////
 80    //
 81    //	PUBLIC PARAMS
 82    //
 83
 84    /**
 85     * Absolute path to a Tempfolder (with trailing slash!).
 86     *  This must be writeable for PHP and also accessible via HTTP,
 87     *  because the image will be stored there.
 88     *
 89     * @var string
 90     *
 91     * @access public
 92     *
 93     */
 94    protected $tempfolder;
 95
 96    /**
 97     *  Absolute path to folder with TrueTypeFonts (with trailing slash!).
 98     *  This must be readable by PHP.
 99     *
100     * @protected string
101     *
102     **/
103    protected $TTF_folder;
104
105    /**
106     *  How many chars the generated text should have
107     *
108     * @type   integer
109     * @access public
110     *
111     **/
112    protected $chars = 5;
113
114    /**
115     *  The minimum size a Char should have
116     *
117     * @type   integer
118     * @access public
119     *
120     **/
121    protected $minsize = 20;
122
123    /**
124     *  The maximum size a Char can have
125     *
126     * @type   integer
127     * @access public
128     *
129     **/
130    protected $maxsize = 30;
131
132    /**
133     *  The maximum degrees a Char should be rotated. Set it to 30 means a random rotation between -30 and 30.
134     *
135     * @type   integer
136     * @access public
137     *
138     **/
139    protected $maxrotation = 25;
140
141    /**
142     *  Background noise On/Off (if is Off, a grid will be created)
143     *
144     * @type   boolean
145     * @access public
146     *
147     **/
148    protected $noise = true;
149
150    /**
151     *  This will only use the 216 websafe color pallette for the image.
152     *
153     * @type   boolean
154     * @access public
155     *
156     **/
157    protected $websafecolors = false;
158
159    /**
160     *  Switches language, available are 'en' and 'de'. You can easily add more. Look in CONSTRUCTOR.
161     *
162     * @type   string
163     * @access public
164     *
165     **/
166    protected $lang = "en";
167
168    /**
169     *  If a user has reached this number of try's without success, he will moved to the $badguys_url
170     *
171     * @type   integer
172     * @access public
173     *
174     **/
175    protected $maxtry = 3;
176
177    /**
178     *  Gives the user the possibility to generate a new captcha-image.
179     *
180     * @type   boolean
181     * @access public
182     *
183     **/
184    protected $refreshlink = true;
185
186    /**
187     *  If a user has reached his maximum try's, he will located to this url.
188     *
189     * @type   boolean
190     * @access public
191     *
192     **/
193    protected $badguys_url = "/";
194
195    /**
196     * Number between 1 and 32
197     *
198     *  Defines the position of 'current try number' in (32-char-length)-string generated by function get_try()
199     *
200     * @type   integer
201     * @access public
202     *
203     **/
204    protected $secretposition = 15;
205
206    /**
207     *  The string is used to generate the md5-key.
208     *
209     * @type   string
210     * @access public
211     *
212     **/
213    protected $secretstring = "A very interesting string like 8 char password!";
214
215    /**
216     *  Outputs configuration values for testing
217     *
218     * @type   boolean
219     * @access public
220     *
221     **/
222    protected $debug = false;
223
224    /** @access public **/
225    public $msg1;
226
227    /** @access public **/
228    public $msg2;
229
230    ////////////////////////////////
231    //
232    //	PRIVATE PARAMS
233    //
234
235    /** @private **/
236    private $lx; // width of picture
237    /** @private **/
238    private $ly; // height of picture
239    /** @private **/
240    private $jpegquality = 80; // image quality
241    /** @private **/
242    private $noisefactor = 9; // this will multiplyed with number of chars
243    /** @private **/
244    private $nb_noise; // number of background-noise-characters
245    /** @private **/
246    private $TTF_file = 'font.ttf'; // holds the current selected TrueTypeFont
247    /** @private **/
248    private $buttontext;
249
250    /** @private **/
251    private $refreshbuttontext;
252
253    /** @private **/
254    private $public_K;
255
256    /** @private **/
257    private $private_K;
258
259    /** @private **/
260    private $key; // md5-key
261    /** @private **/
262    private $public_key; // public key
263    /** @private **/
264    private $filename; // filename of captcha picture
265    /** @private **/
266    private $gd_version; // holds the Version Number of GD-Library
267    /** @private **/
268    // private $QUERY_STRING;		// keeps the ($_GET) Querystring of the original Request
269    /** @private **/
270    private $current_try = 0;
271
272    /** @private **/
273    private $r;
274
275    /** @private **/
276    private $g;
277
278    /** @private **/
279    private $b;
280
281
282    /**
283     * Factory method
284     * Will return Dummy object
285     * in case user does not have required
286     * GD and imagettftext function
287     *
288     * Otherwise will return object of this class
289     *
290     * @param \Lampcms\Config\Ini object
291     *
292     * @return \Lampcms\Captcha\Captcha|\Lampcms\Captcha\CaptchaStub
293     */
294    public static function factory(\Lampcms\Config\Ini $Ini)
295    {
296        d('cp captcha factory');
297        $aConfig = $Ini->getSection('CAPTCHA');
298
299        if (!empty($aConfig['disabled'])) {
300            d('Captcha disabled by administrator. Using Captcha Stub instead');
301
302            return new CaptchaStub();
303        }
304
305        try {
306            self::checkGD();
307            return new self($Ini, $aConfig);
308        } catch ( DevException $e ) {
309            e('Unable to use Captcha because of this error: ' . $e->getMessage());
310
311            return new CaptchaStub();
312        }
313    }
314
315
316    /**
317     * Extracts the config array and generate needed params.
318     *
319     * @param \Lampcms\Config\Ini $Ini
320     * @param array               $config
321     * @param bool                $secure
322     * @param bool                $debug
323     *
324     * @throws \Lampcms\Exception
325     * @throws \Lampcms\DevException
326     */
327    public function __construct(\Lampcms\Config\Ini $Ini, array $config = array(), $secure = true, $debug = false)
328    {
329
330        $this->Ini = $Ini;
331
332        $aConfig = (!empty($config)) ? $config : $Ini->getSection('CAPTCHA');
333        d('Captcha config: ' . \json_encode($aConfig));
334
335
336        d("Captcha-Debug: The available GD-Library has major version " . $this->gd_version);
337
338        $this->tempfolder = LAMPCMS_DATA_DIR . 'img' . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR;
339        $this->TTF_file = LAMPCMS_CONFIG_DIR. DIRECTORY_SEPARATOR .'fonts' . DIRECTORY_SEPARATOR . 'font.ttf';
340
341        d('$this->tempfolder: ' . $this->tempfolder . ' $this->TTF_folder: ' . $this->TTF_folder);
342
343        // Hack prevention
344        if (
345            (isset($_GET['maxtry']) || isset($_POST['maxtry']) || isset($_COOKIE['maxtry']))
346            ||
347            (isset($_GET['debug']) || isset($_POST['debug']) || isset($_COOKIE['debug']))
348            ||
349            (isset($_GET['captcharefresh']) || isset($_COOKIE['captcharefresh']))
350            ||
351            (isset($_POST['captcharefresh']) && isset($_POST['private_key']))
352        ) {
353            d("Captcha-Debug: bad guy detected!");
354            if (isset($this->badguys_url) && !headers_sent()) {
355                header('Location: ' . $this->badguys_url);
356            } else {
357                throw new \Lampcms\Exception('Sorry but something is not right with this captcha image');
358            }
359        }
360
361        // extracts config array
362        if (!empty($aConfig)) {
363            d("Captcha-Debug: Extracts Config-Array in secure-mode!");
364            $valid = get_object_vars($this);
365            // d('valid vars: '.print_r($valid, 1));
366            foreach ($aConfig as $k => $v) {
367                if (array_key_exists($k, $valid)) {
368                    $this->$k = $v; // key/val from $config become instance variables here
369                }
370            }
371        }
372
373        // check vars for maxtry, secretposition and min-max-size
374        $this->maxtry         = ($this->maxtry > 9 || $this->maxtry < 1) ? 3 : $this->maxtry;
375        $this->secretposition = ($this->secretposition > 32 || $this->secretposition < 1) ? $this->maxtry : $this->secretposition;
376        if ($this->minsize > $this->maxsize) {
377            $temp          = $this->minsize;
378            $this->minsize = $this->maxsize;
379            $this->maxsize = $temp;
380            e("What do you think I mean with min and max? Switch minsize with maxsize.");
381        }
382
383        d("Set current TrueType-File: (" . $this->TTF_file . ")");
384        // get number of noise-chars for background if is enabled
385        $this->nb_noise = $this->noise ? ($this->chars * $this->noisefactor) : 0;
386        d("Set number of noise characters to: (" . $this->nb_noise . ")");
387
388
389        // set dimension of image
390        $this->lx = ($this->chars + 1) * (int)(($this->maxsize + $this->minsize) / 1.5);
391        $this->ly = (int)(2.4 * $this->maxsize);
392        d("Set image dimension to: (" . $this->lx . " x " . $this->ly . ")");
393        d("Set messages to language: (" . $this->lang . ")");
394
395
396        // check Postvars
397        if (isset($_POST['public_key'])) {
398            $this->public_K = substr(strip_tags($_POST['public_key']), 0, $this->chars);
399        }
400
401        /**
402         * Replace Z with 0 for submitted captcha text
403         * because we replace 0 with Z when generated image
404         * So now we must make sure to replace it back to 0
405         * str_replace('Z', '0',
406         */
407        if (isset($_POST['private_key'])) {
408            $this->private_K = substr(strip_tags($_POST['private_key']), 0, $this->chars);
409        }
410        $this->current_try = isset($_POST['hncaptcha']) ? $this->get_try() : 0;
411        if (!isset($_POST['captcharefresh'])) {
412            $this->current_try++;
413        }
414
415        d("Check POST-vars, current try is: (" . $this->current_try . ")");
416
417
418        // generate Keys
419        $this->key        = md5($this->secretstring);
420        $this->public_key = substr(md5(uniqid(rand(), true)), 0, $this->chars);
421
422        d('public key is: ' . $this->public_key);
423
424    } // end constructor
425
426
427    /**
428     * validates POST-vars and return result
429     *
430     * @return int 0 = first call | 1 = valid submit | 2 = not valid | 3 = not valid and has reached maximum tries
431     */
432    public function validate_submit()
433    {
434
435        $chk = $this->check_captcha($this->public_K, $this->private_K);
436
437        if ($chk) {
438            d("Captcha-Debug: Captcha is valid, validating submitted form returns: (1)");
439            return 1;
440        } else {
441            if ($this->current_try > $this->maxtry) {
442                d("Captcha-Debug:  Validating submitted form returns: (3)");
443                return 3;
444            } elseif ($this->current_try > 0) {
445                d("Captcha-Debug:  Validating submitted form returns: (2)");
446                return 2;
447            } else {
448                d("Captcha-Debug:  Validating submitted form returns: (0)");
449                return 0;
450            }
451        }
452    }
453
454
455    public function display_captcha()
456    {
457        $this->make_captcha();
458        $is = getimagesize($this->get_filename());
459
460        return "\n" . '<img class="captchapict" src="' . $this->get_filename_url() . '" ' . $is[3] . ' alt="@@This is a captcha-picture. It is used to prevent mass-access by robots. see www.captcha.net@@" title="">' . "\n";
461
462    }
463
464
465    /**
466     *  must be public to work on our project
467     */
468    public function make_captcha()
469    {
470        $private_key = $this->generate_private();
471        d("Captcha-Debug: Generate private key: ($private_key)");
472
473        // create Image and set the appropriate function depending on GD-Version & websafecolor-value
474        if ($this->gd_version >= 2 && !$this->websafecolors) {
475            $func1 = '\\imagecreatetruecolor';
476            $func2 = '\\imagecolorallocate';
477        } else {
478            $func1 = '\\imagecreate';
479            $func2 = '\\imagecolorclosest';
480        }
481        $image = $func1($this->lx, $this->ly);
482        d("Generate ImageStream with: ($func1)");
483        d("For colordefinitions we use: ($func2)");
484        d('$image is: ' . gettype($image));
485
486
487        // Set Backgroundcolor
488        $this->random_color(224, 255);
489        $back = @\imagecolorallocate($image, $this->r, $this->g, $this->b);
490        \imagefilledrectangle($image, 0, 0, $this->lx, $this->ly, $back);
491        d("Captcha-Debug: We allocate one color for Background: (" . $this->r . "-" . $this->g . "-" . $this->b . ")");
492
493        // allocates the 216 websafe color palette to the image
494        if ($this->gd_version < 2 || $this->websafecolors) {
495            $this->makeWebsafeColors($image);
496        }
497
498
499        // fill with noise or grid
500        if ($this->nb_noise > 0) {
501            // random characters in background with random position, angle, color
502            d("Captcha-Debug: Fill background with noise: (" . $this->nb_noise . ")");
503            for ($i = 0; $i < $this->nb_noise; $i++) {
504                //d('Captcha-Debug');
505
506                $size  = (int)(\mt_rand((int)($this->minsize / 2.3), (int)($this->maxsize / 1.7)));
507                $angle = (int)(\mt_rand(0, 360));
508                $x     = (int)(\mt_rand(0, $this->lx));
509                $y     = (int)(\mt_rand(0, (int)($this->ly - ($size / 5))));
510                $this->random_color(160, 224);
511                $color = $func2($image, $this->r, $this->g, $this->b);
512                $text  = chr((int)(\mt_rand(45, 250)));
513                if (false === \imagettftext($image, $size, $angle, $x, $y, $color, $this->TTF_file, $text)) {
514
515                    throw new DevException('Your php does not support imagettftext operation OR your fonts file did not upload correctly (hint: did you upload them in text mode instead of binary?). You should disable captcha support in !config.ini');
516                }
517
518                //d('Captcha-Debug');
519            }
520        } else {
521            // generate grid
522            d("Captcha-Debug: Fill background with x-gridlines: (" . (int)($this->lx / (int)($this->minsize / 1.5)) . ")");
523            for ($i = 0; $i < $this->lx; $i += (int)($this->minsize / 1.5)) {
524                $this->random_color(160, 224);
525                $color = $func2($image, $this->r, $this->g, $this->b);
526                @imageline($image, $i, 0, $i, $this->ly, $color);
527            }
528            d("Captcha-Debug: Fill background with y-gridlines: (" . (int)($this->ly / (int)(($this->minsize / 1.8))) . ")");
529            for ($i = 0; $i < $this->ly; $i += (int)($this->minsize / 1.8)) {
530                $this->random_color(160, 224);
531                $color = $func2($image, $this->r, $this->g, $this->b);
532                @imageline($image, 0, $i, $this->lx, $i, $color);
533            }
534        }
535
536        // generate Text
537        d("Captcha-Debug: Fill foreground with chars and shadows: (" . $this->chars . ")");
538        for ($i = 0, $x = (int)(\mt_rand($this->minsize, $this->maxsize)); $i < $this->chars; $i++) {
539            //d('Captcha-Debug');
540            $text = \strtoupper(\substr($private_key, $i, 1));
541            //d('Captcha-Debug: $text:  '.$text);
542            $angle = (int)(\mt_rand(($this->maxrotation * -1), $this->maxrotation));
543            $size = (int)(\mt_rand($this->minsize, $this->maxsize));
544            $y = (int)(\mt_rand((int)($size * 1.5), (int)($this->ly - ($size / 7))));
545            $this->random_color(0, 127);
546            //d('Captcha-Debug');
547            $color = $func2($image, $this->r, $this->g, $this->b);
548            //d('Captcha-Debug');
549            $this->random_color(0, 127);
550            $shadow = $func2($image, $this->r + 127, $this->g + 127, $this->b + 127);
551            //d('Captcha-Debug');
552            @\imagettftext($image, $size, $angle, $x + (int)($size / 15), $y, $shadow, $this->TTF_file, $text);
553            @\imagettftext($image, $size, $angle, $x, $y - (int)($size / 15), $color, $this->TTF_file, $text);
554            $x += (int)($size + ($this->minsize / 5));
555            //d('Captcha-Debug');
556        }
557
558        d('$image: ' . \gettype($image) . ' image file: ' . $this->get_filename() . ' $this->jpegquality: ' . $this->jpegquality);
559        if (true !== \imagejpeg($image, $this->get_filename(), $this->jpegquality)) {
560            e('error writing captcha image. Make sure your www/w directory and ALL subdirectory have writable permission');
561            throw new DevException('Unable to save captcha-image to ' . $this->get_filename().' Make sure your www/w directory and ALL subdirectory have writable permission');
562        }
563
564        if (!file_exists($this->get_filename())) {
565            e('Unable to save captcha file to ' . $this->get_filename().' Make sure your www/w directory and ALL subdirectory have writable permission');
566            throw new DevException('Unable to save captcha-image to ' . $this->get_filename().' Make sure your www/w directory and ALL subdirectory have writable permission');
567        }
568
569        //d('Captcha-Debug');
570        if (true !== \imagedestroy($image)) {
571            e("Captcha-Debug: Destroy GD Image Resource failed.");
572        }
573        //d('Captcha-Debug');
574    }
575
576
577    /**
578     *
579     * Enter description here ...
580     *
581     * @param unknown_type $image
582     */
583    protected function makeWebsafeColors(&$image)
584    {
585        //$a = array();
586        for ($r = 0; $r <= 255; $r += 51) {
587            for ($g = 0; $g <= 255; $g += 51) {
588                for ($b = 0; $b <= 255; $b += 51) {
589                    $color = imagecolorallocate($image, $r, $g, $b);
590                }
591            }
592        }
593
594        d("Captcha-Debug: Allocate 216 websafe colors to image: (" . imagecolorstotal($image) . ")");
595        //return $a;
596    }
597
598
599    /**
600     *
601     * Enter description here ...
602     *
603     * @param int $min
604     * @param int $max
605     */
606    protected function random_color($min, $max)
607    {
608        $this->r = (int)(mt_rand($min, $max));
609        $this->g = (int)(mt_rand($min, $max));
610        $this->b = (int)(mt_rand($min, $max));
611    }
612
613
614    /**
615     * Check captcha
616     *
617     * @param string $public  public key
618     * @param string $private private key
619     *
620     * @return bool
621     */
622    protected function check_captcha($public, $private)
623    {
624        $res = false;
625        /**
626         * when check, destroy picture on disk
627         */
628        if (file_exists($this->get_filename($public))) {
629            $ret = @unlink($this->get_filename($public)) ? true : false;
630            if ($this->debug) {
631                d("Captcha-Debug: Delete image (" . $this->get_filename($public) . ") returns: ($ret)");
632            }
633
634            $res = (strtolower($private) == strtolower($this->generate_private($public)));
635
636            d("Captcha-Debug: Comparing public with private key returns: ($res)");
637            d('PRIV: ' . strtolower($private) . " ? Generated-PRIVATE (from public=$public) : " . strtolower($this->generate_private($public)));
638
639        } else {
640
641            e('Captcha-Debug: file does not exist ' . $this->get_filename($public));
642        }
643
644        return $res;
645    }
646
647
648    /**
649     * must be public for Lampcms project
650     *
651     * @param string $public
652     *
653     * @return string
654     */
655    public function get_filename($public = "")
656    {
657        if ($public == "") {
658            $public = $this->public_key;
659        }
660
661        return $this->tempfolder . $public . ".jpg";
662    }
663
664
665    /**
666     *
667     *
668     * @param string $public
669     *
670     * @return string
671     */
672    public function get_filename_url($public = "")
673    {
674        if ($public == "") {
675            $public = $this->public_key;
676        }
677
678        return '{_DIR_}/w/img/tmp/' . $public . ".jpg";
679    }
680
681
682    /**
683     *
684     * @return array with 3 elements: src = the web path (relative),
685     * 'w' = width, 'h' = height
686     */
687    public function getCaptchaImage()
688    {
689        $this->make_captcha();
690        $is = getimagesize($this->tempfolder . $this->public_key . '.jpg');
691
692        return array('src' => '{_DIR_}/w/img/tmp/' . $this->public_key . '.jpg',
693                     'w'   => $is[0],
694                     'h'   => $is[1]);
695    }
696
697
698    /**
699     * Must be public to work on Lampcms project
700     *
701     * @param bool $in
702     *
703     * @return int|string
704     */
705    public function get_try($in = true)
706    {
707        $s = array();
708        for ($i = 1; $i <= $this->maxtry; $i++) {
709            $s[$i] = $i;
710        }
711
712        if ($in) {
713            return (int)substr(\strip_tags($_POST['hncaptcha']), ($this->secretposition - 1), 1);
714        } else {
715            $a = "";
716            $b = "";
717            for ($i = 1; $i < $this->secretposition; $i++) {
718                $a .= $s[(int)(\mt_rand(1, $this->maxtry))];
719            }
720            for ($i = 0; $i < (32 - $this->secretposition); $i++) {
721                $b .= $s[(int)(\mt_rand(1, $this->maxtry))];
722            }
723
724            return $a . $this->current_try . $b;
725        }
726    }
727
728
729    /**
730     * Get version of php GD extension
731     *
732     * @return version of GD
733     *
734     * @throws \Lampcms\DevException if GD not available
735     * or not compiled with imatettftext support
736     * or does not have JPEG support
737     */
738    public static function checkGD()
739    {
740        if (!extension_loaded('gd')) {
741            throw new DevException('GD module not loaded. Cannot use Captcha class without GD library. Check your php info');
742        }
743
744        if (!function_exists('imagettftext')) {
745            throw new DevException('Your php installation does not have the "imagettftext" function. Captcha cannot be used without this function. ');
746        }
747
748        $gd_info = gd_info();
749
750        if (empty($gd_info['JPG Support']) && empty($gd_info['JPEG Support'])) {
751            throw new DevException('Your php GD version does not have support for JPG image. Captcha cannot be used without JPG support in GD');
752        }
753
754        if (empty($gd_info['GD Version'])) {
755            throw new DevException('Unknown version of GD. Unable to use Captcha');
756        }
757
758        $gdv = $gd_info['GD Version'];
759        d('$gdv: ' . $gdv);
760        $Version = \preg_replace('/[[:alpha:][:space:]()]+/', '', $gdv);
761        d('Version: ' . $Version);
762
763        if (version_compare($Version, 2.0) < 0) {
764            throw new DevException('GD version must be newer than 2.0. Your installed version is: ' . $Version . ' Captcha will not be used');
765        }
766
767        return $Version;
768    }
769
770
771    /**
772     *
773     * The private key will be used
774     * for the actual image of captcha
775     *
776     * @param string $public
777     *
778     * @return mixed|string
779     */
780    protected function generate_private($public = "")
781    {
782        if ($public == "") {
783            $public = $this->public_key;
784        }
785
786        d("public key WHICH USED for generate private key $public");
787
788        $key = \substr(md5($this->key . $public), 16 - $this->chars / 2, $this->chars);
789        /**
790         * 0 will be replaced with Z
791         * because 0 renders badly in our font
792         *
793         */
794        $key = \str_replace('0', 'Z', $key);
795
796        return $key;
797    }
798
799
800    /**
801     * getter method to get public_key
802     * which is a private var
803     *
804     * @return string value of public_key
805     */
806    public function getPublicKey()
807    {
808        return $this->public_key;
809    }
810
811
812    /**
813     * Returns array of values that we need for
814     * the Captcha form.
815     *
816     * @return array with 'img' => complete html of img tag,
817     * 'public_key' value of public key,
818     * 'hncaptcha' value of hncaptcha
819     */
820    public function getCaptchaArray()
821    {
822        $aRet = array(
823            'img'        => $this->display_captcha(),
824            'public_key' => $this->getPublicKey(),
825            'hncaptcha'  => $this->get_try(false));
826
827        return $aRet;
828    }
829
830
831    /**
832     *
833     * Create block with Label, Image and
834     * input for captcha entry
835     *
836     * This block can just be dropped into an html template
837     * of any form
838     *
839     * @return string html block with Captcha
840     */
841    public function getCaptchaBlock()
842    {
843        $aVals = $this->getCaptchaArray();
844        d('got captcha vals: ' . \json_encode($aVals));
845
846        $s = \tplCaptcha::parse($aVals, false);
847
848        return $s;
849    }
850
851}