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