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