xref: /plugin/captcha/helper.php (revision 52e95008dde459d7576110b624ca9be489f50ce7)
177e00bf9SAndreas Gohr<?php
277e00bf9SAndreas Gohr/**
377e00bf9SAndreas Gohr * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
477e00bf9SAndreas Gohr * @author     Andreas Gohr <andi@splitbrain.org>
577e00bf9SAndreas Gohr */
677e00bf9SAndreas Gohr// must be run within Dokuwiki
777e00bf9SAndreas Gohrif(!defined('DOKU_INC')) die();
877e00bf9SAndreas Gohrif(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
977e00bf9SAndreas Gohrrequire_once(DOKU_INC.'inc/blowfish.php');
1077e00bf9SAndreas Gohr
1177e00bf9SAndreas Gohrclass helper_plugin_captcha extends DokuWiki_Plugin {
1277e00bf9SAndreas Gohr
1377e00bf9SAndreas Gohr    /**
1477e00bf9SAndreas Gohr     * return some info
1577e00bf9SAndreas Gohr     */
1677e00bf9SAndreas Gohr    function getInfo(){
1777e00bf9SAndreas Gohr        return confToHash(dirname(__FILE__).'/info.txt');
1877e00bf9SAndreas Gohr    }
1977e00bf9SAndreas Gohr
2077e00bf9SAndreas Gohr    /**
2177e00bf9SAndreas Gohr     * Check if the CAPTCHA should be used. Always check this before using the methods below.
2277e00bf9SAndreas Gohr     *
2377e00bf9SAndreas Gohr     * @return bool true when the CAPTCHA should be used
2477e00bf9SAndreas Gohr     */
2577e00bf9SAndreas Gohr    function isEnabled(){
2677e00bf9SAndreas Gohr        if(!$this->getConf('forusers') && $_SERVER['REMOTE_USER']) return false;
2777e00bf9SAndreas Gohr        return true;
2877e00bf9SAndreas Gohr    }
2977e00bf9SAndreas Gohr
3077e00bf9SAndreas Gohr    /**
3177e00bf9SAndreas Gohr     * Returns the HTML to display the CAPTCHA with the chosen method
3277e00bf9SAndreas Gohr     */
3377e00bf9SAndreas Gohr    function getHTML(){
3477e00bf9SAndreas Gohr        global $ID;
3577e00bf9SAndreas Gohr
3677e00bf9SAndreas Gohr        $rand = (float) (rand(0,10000))/10000;
3777e00bf9SAndreas Gohr        $code = $this->_generateCAPTCHA($this->_fixedIdent(),$rand);
3877e00bf9SAndreas Gohr        $secret = PMA_blowfish_encrypt($rand,auth_cookiesalt());
3977e00bf9SAndreas Gohr
4077e00bf9SAndreas Gohr        $out  = '';
4177e00bf9SAndreas Gohr        $out .= '<div id="plugin__captcha_wrapper">';
4277e00bf9SAndreas Gohr        $out .= '<input type="hidden" name="plugin__captcha_secret" value="'.hsc($secret).'" />';
4377e00bf9SAndreas Gohr        $out .= '<label for="plugin__captcha">'.$this->getLang('fillcaptcha').'</label> ';
4477e00bf9SAndreas Gohr        $out .= '<input type="text" size="5" maxlength="5" name="plugin__captcha" id="plugin__captcha" class="edit" /> ';
4577e00bf9SAndreas Gohr        switch($this->getConf('mode')){
4677e00bf9SAndreas Gohr            case 'text':
4777e00bf9SAndreas Gohr                $out .= $code;
4877e00bf9SAndreas Gohr                break;
4977e00bf9SAndreas Gohr            case 'js':
5077e00bf9SAndreas Gohr                $out .= '<span id="plugin__captcha_code">'.$code.'</span>';
5177e00bf9SAndreas Gohr                break;
5277e00bf9SAndreas Gohr            case 'image':
5377e00bf9SAndreas Gohr                $out .= '<img src="'.DOKU_BASE.'lib/plugins/captcha/img.php?secret='.rawurlencode($secret).'&amp;id='.$ID.'" '.
5477e00bf9SAndreas Gohr                        ' width="'.$this->getConf('width').'" height="'.$this->getConf('height').'" alt="" /> ';
5577e00bf9SAndreas Gohr                break;
5677e00bf9SAndreas Gohr            case 'audio':
5777e00bf9SAndreas Gohr                $out .= '<img src="'.DOKU_BASE.'lib/plugins/captcha/img.php?secret='.rawurlencode($secret).'&amp;id='.$ID.'" '.
5877e00bf9SAndreas Gohr                        ' width="'.$this->getConf('width').'" height="'.$this->getConf('height').'" alt="" /> ';
5977e00bf9SAndreas Gohr                $out .= '<a href="'.DOKU_BASE.'lib/plugins/captcha/wav.php?secret='.rawurlencode($secret).'&amp;id='.$ID.'"'.
6077e00bf9SAndreas Gohr                        ' class="JSnocheck" title="'.$this->getLang('soundlink').'">';
6177e00bf9SAndreas Gohr                $out .= '<img src="'.DOKU_BASE.'lib/plugins/captcha/sound.png" width="16" height="16"'.
6277e00bf9SAndreas Gohr                        ' alt="'.$this->getLang('soundlink').'" /></a>';
6377e00bf9SAndreas Gohr                break;
64*52e95008SAndreas Gohr            case 'figlet':
65*52e95008SAndreas Gohr                require_once(dirname(__FILE__).'/figlet.php');
66*52e95008SAndreas Gohr                $figlet = new phpFiglet();
67*52e95008SAndreas Gohr                if($figlet->loadfont(dirname(__FILE__).'/figlet.flf')){
68*52e95008SAndreas Gohr                    $out .= '<pre>';
69*52e95008SAndreas Gohr                    $out .= rtrim($figlet->fetch($code));
70*52e95008SAndreas Gohr                    $out .= '</pre>';
71*52e95008SAndreas Gohr                }else{
72*52e95008SAndreas Gohr                    msg('Failed to load figlet.flf font file. CAPTCHA broken',-1);
73*52e95008SAndreas Gohr                }
74*52e95008SAndreas Gohr                break;
7577e00bf9SAndreas Gohr        }
7677e00bf9SAndreas Gohr        $out .= '</div>';
7777e00bf9SAndreas Gohr        return $out;
7877e00bf9SAndreas Gohr    }
7977e00bf9SAndreas Gohr
8077e00bf9SAndreas Gohr    /**
8177e00bf9SAndreas Gohr     * Checks if the the CAPTCHA was solved correctly
8277e00bf9SAndreas Gohr     *
8377e00bf9SAndreas Gohr     * @param  bool $msg when true, an error will be signalled through the msg() method
8477e00bf9SAndreas Gohr     * @return bool true when the answer was correct, otherwise false
8577e00bf9SAndreas Gohr     */
8677e00bf9SAndreas Gohr    function check($msg=true){
8777e00bf9SAndreas Gohr        // compare provided string with decrypted captcha
8877e00bf9SAndreas Gohr        $rand = PMA_blowfish_decrypt($_REQUEST['plugin__captcha_secret'],auth_cookiesalt());
8977e00bf9SAndreas Gohr        $code = $this->_generateCAPTCHA($this->_fixedIdent(),$rand);
9077e00bf9SAndreas Gohr
9177e00bf9SAndreas Gohr        if(!$_REQUEST['plugin__captcha_secret'] ||
9277e00bf9SAndreas Gohr           !$_REQUEST['plugin__captcha'] ||
9377e00bf9SAndreas Gohr           strtoupper($_REQUEST['plugin__captcha']) != $code){
9477e00bf9SAndreas Gohr            if($msg) msg($this->getLang('testfailed'),-1);
9577e00bf9SAndreas Gohr            return false;
9677e00bf9SAndreas Gohr        }
9777e00bf9SAndreas Gohr        return true;
9877e00bf9SAndreas Gohr    }
9977e00bf9SAndreas Gohr
10077e00bf9SAndreas Gohr    /**
10177e00bf9SAndreas Gohr     * Build a semi-secret fixed string identifying the current page and user
10277e00bf9SAndreas Gohr     *
10377e00bf9SAndreas Gohr     * This string is always the same for the current user when editing the same
10477e00bf9SAndreas Gohr     * page revision.
10577e00bf9SAndreas Gohr     */
10677e00bf9SAndreas Gohr    function _fixedIdent(){
10777e00bf9SAndreas Gohr        global $ID;
10877e00bf9SAndreas Gohr        $lm = @filemtime(wikiFN($ID));
10977e00bf9SAndreas Gohr        return auth_browseruid().
11077e00bf9SAndreas Gohr               auth_cookiesalt().
11177e00bf9SAndreas Gohr               $ID.$lm;
11277e00bf9SAndreas Gohr    }
11377e00bf9SAndreas Gohr
11477e00bf9SAndreas Gohr    /**
11577e00bf9SAndreas Gohr     * Generates a random 5 char string
11677e00bf9SAndreas Gohr     *
11777e00bf9SAndreas Gohr     * @param $fixed string - the fixed part, any string
11877e00bf9SAndreas Gohr     * @param $rand  float  - some random number between 0 and 1
11977e00bf9SAndreas Gohr     */
12077e00bf9SAndreas Gohr    function _generateCAPTCHA($fixed,$rand){
12177e00bf9SAndreas Gohr        $fixed = hexdec(substr(md5($fixed),5,5)); // use part of the md5 to generate an int
12277e00bf9SAndreas Gohr        $numbers = md5($rand * $fixed); // combine both values
12377e00bf9SAndreas Gohr
12477e00bf9SAndreas Gohr        // now create the letters
12577e00bf9SAndreas Gohr        $code = '';
12677e00bf9SAndreas Gohr        for($i=0;$i<10;$i+=2){
12777e00bf9SAndreas Gohr            $code .= chr(floor(hexdec($numbers[$i].$numbers[$i+1])/10) + 65);
12877e00bf9SAndreas Gohr        }
12977e00bf9SAndreas Gohr
13077e00bf9SAndreas Gohr        return $code;
13177e00bf9SAndreas Gohr    }
13277e00bf9SAndreas Gohr
13377e00bf9SAndreas Gohr
13477e00bf9SAndreas Gohr    /**
13577e00bf9SAndreas Gohr     * Create a CAPTCHA image
13677e00bf9SAndreas Gohr     */
13777e00bf9SAndreas Gohr    function _imageCAPTCHA($text){
13877e00bf9SAndreas Gohr        $w = $this->getConf('width');
13977e00bf9SAndreas Gohr        $h = $this->getConf('height');
14077e00bf9SAndreas Gohr
14177e00bf9SAndreas Gohr        // create a white image
14277e00bf9SAndreas Gohr        $img = imagecreate($w, $h);
14377e00bf9SAndreas Gohr        imagecolorallocate($img, 255, 255, 255);
14477e00bf9SAndreas Gohr
14577e00bf9SAndreas Gohr        // add some lines as background noise
14677e00bf9SAndreas Gohr        for ($i = 0; $i < 30; $i++) {
14777e00bf9SAndreas Gohr            $color = imagecolorallocate($img,rand(100, 250),rand(100, 250),rand(100, 250));
14877e00bf9SAndreas Gohr            imageline($img,rand(0,$w),rand(0,$h),rand(0,$w),rand(0,$h),$color);
14977e00bf9SAndreas Gohr        }
15077e00bf9SAndreas Gohr
15177e00bf9SAndreas Gohr        // draw the letters
15277e00bf9SAndreas Gohr        for ($i = 0; $i < strlen($text); $i++){
15377e00bf9SAndreas Gohr            $font  = dirname(__FILE__).'/VeraSe.ttf';
15477e00bf9SAndreas Gohr            $color = imagecolorallocate($img, rand(0, 100), rand(0, 100), rand(0, 100));
15577e00bf9SAndreas Gohr            $size  = rand(floor($h/1.8),floor($h*0.7));
15677e00bf9SAndreas Gohr            $angle = rand(-35, 35);
15777e00bf9SAndreas Gohr
15877e00bf9SAndreas Gohr            $x = ($w*0.05) +  $i * floor($w*0.9/5);
15977e00bf9SAndreas Gohr            $cheight = $size + ($size*0.5);
16077e00bf9SAndreas Gohr            $y = floor($h / 2 + $cheight / 3.8);
16177e00bf9SAndreas Gohr
16277e00bf9SAndreas Gohr            imagettftext($img, $size, $angle, $x, $y, $color, $font, $text[$i]);
16377e00bf9SAndreas Gohr        }
16477e00bf9SAndreas Gohr
16577e00bf9SAndreas Gohr        header("Content-type: image/png");
16677e00bf9SAndreas Gohr        imagepng($img);
16777e00bf9SAndreas Gohr        imagedestroy($img);
16877e00bf9SAndreas Gohr    }
16977e00bf9SAndreas Gohr
17077e00bf9SAndreas Gohr
17177e00bf9SAndreas Gohr}
172