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).'&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).'&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).'&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