1<?php 2/** 3 * CAPTCHA antispam plugin 4 * 5 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 6 * @author Andreas Gohr <gohr@cosmocode.de> 7 */ 8 9// must be run within Dokuwiki 10if(!defined('DOKU_INC')) die(); 11if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 12 13class action_plugin_captcha extends DokuWiki_Action_Plugin { 14 15 /** 16 * register the eventhandlers 17 */ 18 public function register(Doku_Event_Handler $controller) { 19 // check CAPTCHA success 20 $controller->register_hook( 21 'ACTION_ACT_PREPROCESS', 22 'BEFORE', 23 $this, 24 'handle_captcha_input', 25 array() 26 ); 27 28 // inject in edit form 29 $controller->register_hook( 30 'HTML_EDITFORM_OUTPUT', 31 'BEFORE', 32 $this, 33 'handle_form_output', 34 array() 35 ); 36 37 // inject in user registration 38 $controller->register_hook( 39 'HTML_REGISTERFORM_OUTPUT', 40 'BEFORE', 41 $this, 42 'handle_form_output', 43 array() 44 ); 45 46 // inject in password reset 47 $controller->register_hook( 48 'HTML_RESENDPWDFORM_OUTPUT', 49 'BEFORE', 50 $this, 51 'handle_form_output', 52 array() 53 ); 54 55 if($this->getConf('loginprotect')) { 56 // inject in login form 57 $controller->register_hook( 58 'HTML_LOGINFORM_OUTPUT', 59 'BEFORE', 60 $this, 61 'handle_form_output', 62 array() 63 ); 64 // check on login 65 $controller->register_hook( 66 'AUTH_LOGIN_CHECK', 67 'BEFORE', 68 $this, 69 'handle_login', 70 array() 71 ); 72 } 73 } 74 75 /** 76 * Check if the current mode should be handled by CAPTCHA 77 * 78 * Note: checking needs to be done when a form has been submitted, not when the form 79 * is shown for the first time. Except for the editing process this is not determined 80 * by $act alone but needs to inspect other input variables. 81 * 82 * @param string $act cleaned action mode 83 * @return bool 84 */ 85 protected function needs_checking($act) { 86 global $INPUT; 87 88 switch($act) { 89 case 'save': 90 return true; 91 case 'register': 92 case 'resendpwd': 93 return $INPUT->bool('save'); 94 case 'login': 95 // we do not handle this here, but in handle_login() 96 default: 97 return false; 98 } 99 } 100 101 /** 102 * Aborts the given mode 103 * 104 * Aborting depends on the mode. It might unset certain input parameters or simply switch 105 * the mode to something else (giving as return which needs to be passed back to the 106 * ACTION_ACT_PREPROCESS event) 107 * 108 * @param string $act cleaned action mode 109 * @return string the new mode to use 110 */ 111 protected function abort_action($act) { 112 global $INPUT; 113 114 switch($act) { 115 case 'save': 116 return 'preview'; 117 case 'register': 118 case 'resendpwd': 119 $INPUT->post->set('save', false); 120 return $act; 121 case 'login': 122 // we do not handle this here, but in handle_login() 123 default: 124 return $act; 125 } 126 } 127 128 /** 129 * Handles CAPTCHA check in login 130 * 131 * Logins happen very early in the DokuWiki lifecycle, so we have to intercept them 132 * in their own event. 133 * 134 * @param Doku_Event $event 135 * @param $param 136 */ 137 public function handle_login(Doku_Event $event, $param) { 138 global $INPUT; 139 if(!$this->getConf('loginprotect')) return; // no protection wanted 140 if(!$INPUT->bool('u')) return; // this login was not triggered by a form 141 142 // we need to have $ID set for the captcha check 143 global $ID; 144 $ID = getID(); 145 146 /** @var helper_plugin_captcha $helper */ 147 $helper = plugin_load('helper', 'captcha'); 148 if(!$helper->check()) { 149 $event->data['silent'] = true; // we have our own message 150 $event->result = false; // login fail 151 $event->preventDefault(); 152 $event->stopPropagation(); 153 } 154 } 155 156 /** 157 * Intercept all actions and check for CAPTCHA first. 158 */ 159 public function handle_captcha_input(Doku_Event $event, $param) { 160 $act = act_clean($event->data); 161 if(!$this->needs_checking($act)) return; 162 163 // do nothing if logged in user and no CAPTCHA required 164 if(!$this->getConf('forusers') && $_SERVER['REMOTE_USER']) { 165 return; 166 } 167 168 // check captcha 169 /** @var helper_plugin_captcha $helper */ 170 $helper = plugin_load('helper', 'captcha'); 171 if(!$helper->check()) { 172 $event->data = $this->abort_action($act); 173 } 174 } 175 176 /** 177 * Inject the CAPTCHA in a DokuForm 178 */ 179 public function handle_form_output(Doku_Event $event, $param) { 180 // get position of submit button 181 $pos = $event->data->findElementByAttribute('type', 'submit'); 182 if(!$pos) return; // no button -> source view mode 183 184 // do nothing if logged in user and no CAPTCHA required 185 if(!$this->getConf('forusers') && $_SERVER['REMOTE_USER']) { 186 return; 187 } 188 189 // get the CAPTCHA 190 /** @var helper_plugin_captcha $helper */ 191 $helper = plugin_load('helper', 'captcha'); 192 $out = $helper->getHTML(); 193 194 // new wiki - insert after the submit button 195 $event->data->insertElement($pos + 1, $out); 196 } 197 198} 199 200