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 } 65 $out .= '</div>'; 66 return $out; 67 } 68 69 /** 70 * Checks if the the CAPTCHA was solved correctly 71 * 72 * @param bool $msg when true, an error will be signalled through the msg() method 73 * @return bool true when the answer was correct, otherwise false 74 */ 75 function check($msg=true){ 76 // compare provided string with decrypted captcha 77 $rand = PMA_blowfish_decrypt($_REQUEST['plugin__captcha_secret'],auth_cookiesalt()); 78 $code = $this->_generateCAPTCHA($this->_fixedIdent(),$rand); 79 80 if(!$_REQUEST['plugin__captcha_secret'] || 81 !$_REQUEST['plugin__captcha'] || 82 strtoupper($_REQUEST['plugin__captcha']) != $code){ 83 if($msg) msg($this->getLang('testfailed'),-1); 84 return false; 85 } 86 return true; 87 } 88 89 /** 90 * Build a semi-secret fixed string identifying the current page and user 91 * 92 * This string is always the same for the current user when editing the same 93 * page revision. 94 */ 95 function _fixedIdent(){ 96 global $ID; 97 $lm = @filemtime(wikiFN($ID)); 98 return auth_browseruid(). 99 auth_cookiesalt(). 100 $ID.$lm; 101 } 102 103 /** 104 * Generates a random 5 char string 105 * 106 * @param $fixed string - the fixed part, any string 107 * @param $rand float - some random number between 0 and 1 108 */ 109 function _generateCAPTCHA($fixed,$rand){ 110 $fixed = hexdec(substr(md5($fixed),5,5)); // use part of the md5 to generate an int 111 $numbers = md5($rand * $fixed); // combine both values 112 113 // now create the letters 114 $code = ''; 115 for($i=0;$i<10;$i+=2){ 116 $code .= chr(floor(hexdec($numbers[$i].$numbers[$i+1])/10) + 65); 117 } 118 119 return $code; 120 } 121 122 123 /** 124 * Create a CAPTCHA image 125 */ 126 function _imageCAPTCHA($text){ 127 $w = $this->getConf('width'); 128 $h = $this->getConf('height'); 129 130 // create a white image 131 $img = imagecreate($w, $h); 132 imagecolorallocate($img, 255, 255, 255); 133 134 // add some lines as background noise 135 for ($i = 0; $i < 30; $i++) { 136 $color = imagecolorallocate($img,rand(100, 250),rand(100, 250),rand(100, 250)); 137 imageline($img,rand(0,$w),rand(0,$h),rand(0,$w),rand(0,$h),$color); 138 } 139 140 // draw the letters 141 for ($i = 0; $i < strlen($text); $i++){ 142 $font = dirname(__FILE__).'/VeraSe.ttf'; 143 $color = imagecolorallocate($img, rand(0, 100), rand(0, 100), rand(0, 100)); 144 $size = rand(floor($h/1.8),floor($h*0.7)); 145 $angle = rand(-35, 35); 146 147 $x = ($w*0.05) + $i * floor($w*0.9/5); 148 $cheight = $size + ($size*0.5); 149 $y = floor($h / 2 + $cheight / 3.8); 150 151 imagettftext($img, $size, $angle, $x, $y, $color, $font, $text[$i]); 152 } 153 154 header("Content-type: image/png"); 155 imagepng($img); 156 imagedestroy($img); 157 } 158 159 160} 161