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