xref: /plugin/captcha/action.php (revision 643f15bdb4f70ed41444d2b6d19e24f3d640baf4)
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