xref: /plugin/captcha/helper.php (revision 52e95008dde459d7576110b624ca9be489f50ce7)
1<?php
2/**
3 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
4 * @author     Andreas Gohr <andi@splitbrain.org>
5 */
6// must be run within Dokuwiki
7if(!defined('DOKU_INC')) die();
8if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
9require_once(DOKU_INC.'inc/blowfish.php');
10
11class helper_plugin_captcha extends DokuWiki_Plugin {
12
13    /**
14     * return some info
15     */
16    function getInfo(){
17        return confToHash(dirname(__FILE__).'/info.txt');
18    }
19
20    /**
21     * Check if the CAPTCHA should be used. Always check this before using the methods below.
22     *
23     * @return bool true when the CAPTCHA should be used
24     */
25    function isEnabled(){
26        if(!$this->getConf('forusers') && $_SERVER['REMOTE_USER']) return false;
27        return true;
28    }
29
30    /**
31     * Returns the HTML to display the CAPTCHA with the chosen method
32     */
33    function getHTML(){
34        global $ID;
35
36        $rand = (float) (rand(0,10000))/10000;
37        $code = $this->_generateCAPTCHA($this->_fixedIdent(),$rand);
38        $secret = PMA_blowfish_encrypt($rand,auth_cookiesalt());
39
40        $out  = '';
41        $out .= '<div id="plugin__captcha_wrapper">';
42        $out .= '<input type="hidden" name="plugin__captcha_secret" value="'.hsc($secret).'" />';
43        $out .= '<label for="plugin__captcha">'.$this->getLang('fillcaptcha').'</label> ';
44        $out .= '<input type="text" size="5" maxlength="5" name="plugin__captcha" id="plugin__captcha" class="edit" /> ';
45        switch($this->getConf('mode')){
46            case 'text':
47                $out .= $code;
48                break;
49            case 'js':
50                $out .= '<span id="plugin__captcha_code">'.$code.'</span>';
51                break;
52            case 'image':
53                $out .= '<img src="'.DOKU_BASE.'lib/plugins/captcha/img.php?secret='.rawurlencode($secret).'&amp;id='.$ID.'" '.
54                        ' width="'.$this->getConf('width').'" height="'.$this->getConf('height').'" alt="" /> ';
55                break;
56            case 'audio':
57                $out .= '<img src="'.DOKU_BASE.'lib/plugins/captcha/img.php?secret='.rawurlencode($secret).'&amp;id='.$ID.'" '.
58                        ' width="'.$this->getConf('width').'" height="'.$this->getConf('height').'" alt="" /> ';
59                $out .= '<a href="'.DOKU_BASE.'lib/plugins/captcha/wav.php?secret='.rawurlencode($secret).'&amp;id='.$ID.'"'.
60                        ' class="JSnocheck" title="'.$this->getLang('soundlink').'">';
61                $out .= '<img src="'.DOKU_BASE.'lib/plugins/captcha/sound.png" width="16" height="16"'.
62                        ' alt="'.$this->getLang('soundlink').'" /></a>';
63                break;
64            case 'figlet':
65                require_once(dirname(__FILE__).'/figlet.php');
66                $figlet = new phpFiglet();
67                if($figlet->loadfont(dirname(__FILE__).'/figlet.flf')){
68                    $out .= '<pre>';
69                    $out .= rtrim($figlet->fetch($code));
70                    $out .= '</pre>';
71                }else{
72                    msg('Failed to load figlet.flf font file. CAPTCHA broken',-1);
73                }
74                break;
75        }
76        $out .= '</div>';
77        return $out;
78    }
79
80    /**
81     * Checks if the the CAPTCHA was solved correctly
82     *
83     * @param  bool $msg when true, an error will be signalled through the msg() method
84     * @return bool true when the answer was correct, otherwise false
85     */
86    function check($msg=true){
87        // compare provided string with decrypted captcha
88        $rand = PMA_blowfish_decrypt($_REQUEST['plugin__captcha_secret'],auth_cookiesalt());
89        $code = $this->_generateCAPTCHA($this->_fixedIdent(),$rand);
90
91        if(!$_REQUEST['plugin__captcha_secret'] ||
92           !$_REQUEST['plugin__captcha'] ||
93           strtoupper($_REQUEST['plugin__captcha']) != $code){
94            if($msg) msg($this->getLang('testfailed'),-1);
95            return false;
96        }
97        return true;
98    }
99
100    /**
101     * Build a semi-secret fixed string identifying the current page and user
102     *
103     * This string is always the same for the current user when editing the same
104     * page revision.
105     */
106    function _fixedIdent(){
107        global $ID;
108        $lm = @filemtime(wikiFN($ID));
109        return auth_browseruid().
110               auth_cookiesalt().
111               $ID.$lm;
112    }
113
114    /**
115     * Generates a random 5 char string
116     *
117     * @param $fixed string - the fixed part, any string
118     * @param $rand  float  - some random number between 0 and 1
119     */
120    function _generateCAPTCHA($fixed,$rand){
121        $fixed = hexdec(substr(md5($fixed),5,5)); // use part of the md5 to generate an int
122        $numbers = md5($rand * $fixed); // combine both values
123
124        // now create the letters
125        $code = '';
126        for($i=0;$i<10;$i+=2){
127            $code .= chr(floor(hexdec($numbers[$i].$numbers[$i+1])/10) + 65);
128        }
129
130        return $code;
131    }
132
133
134    /**
135     * Create a CAPTCHA image
136     */
137    function _imageCAPTCHA($text){
138        $w = $this->getConf('width');
139        $h = $this->getConf('height');
140
141        // create a white image
142        $img = imagecreate($w, $h);
143        imagecolorallocate($img, 255, 255, 255);
144
145        // add some lines as background noise
146        for ($i = 0; $i < 30; $i++) {
147            $color = imagecolorallocate($img,rand(100, 250),rand(100, 250),rand(100, 250));
148            imageline($img,rand(0,$w),rand(0,$h),rand(0,$w),rand(0,$h),$color);
149        }
150
151        // draw the letters
152        for ($i = 0; $i < strlen($text); $i++){
153            $font  = dirname(__FILE__).'/VeraSe.ttf';
154            $color = imagecolorallocate($img, rand(0, 100), rand(0, 100), rand(0, 100));
155            $size  = rand(floor($h/1.8),floor($h*0.7));
156            $angle = rand(-35, 35);
157
158            $x = ($w*0.05) +  $i * floor($w*0.9/5);
159            $cheight = $size + ($size*0.5);
160            $y = floor($h / 2 + $cheight / 3.8);
161
162            imagettftext($img, $size, $angle, $x, $y, $color, $font, $text[$i]);
163        }
164
165        header("Content-type: image/png");
166        imagepng($img);
167        imagedestroy($img);
168    }
169
170
171}
172