1<?php
2if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../../').'/');
3if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
4require_once(DOKU_PLUGIN.'action.php');
5
6/**
7 * @license    GPL 2 (http://www.gnu.org/licenses/gpl2.html)
8 * @author     Adrian Schlegel <adrian.schlegel@liip.ch>
9 * @author     Robert Bronsdon <reashlin@gmail.com>
10 */
11
12class action_plugin_recaptcha extends DokuWiki_Action_Plugin {
13
14    private $recaptchaLangs = array('en', 'nl', 'fr', 'de', 'pt', 'ru', 'es', 'tr');
15
16    /**
17     * register an event hook
18     *
19     */
20    function register(Doku_Event_Handler $controller)
21    {
22        // only register the hooks if the necessary config paramters exist
23        if($this->getConf('publickey') && $this->getConf('privatekey')) {
24            if($this->getConf('regprotect') || $this->getConf('editprotect')){
25                    $controller->register_hook('ACTION_ACT_PREPROCESS',
26                                               'BEFORE',
27                                               $this,
28                                               'preprocess',
29                                               array());
30            }
31            if($this->getConf('regprotect')){
32                // new hook
33                $controller->register_hook('HTML_REGISTERFORM_OUTPUT',
34                                           'BEFORE',
35                                           $this,
36                                           'insert',
37                                           array('oldhook' => false));
38                // old hook
39                $controller->register_hook('HTML_REGISTERFORM_INJECTION',
40                                           'BEFORE',
41                                           $this,
42                                           'insert',
43                                           array('oldhook' => true));
44            }
45            if($this->getConf('editprotect')){
46                // old hook
47                $controller->register_hook('HTML_EDITFORM_INJECTION',
48                                           'BEFORE',
49                                           $this,
50                                           'editform_output',
51                                           array('editform' => true, 'oldhook' => true));
52                // new hook
53                $controller->register_hook('HTML_EDITFORM_OUTPUT',
54                                           'BEFORE',
55                                           $this,
56                                           'editform_output',
57                                           array('editform' => true, 'oldhook' => false));
58            }
59        }
60    }
61
62    /**
63     * Add reCAPTCHA to edit fields
64     *
65     * @param obj $event
66     * @param array $param
67     */
68    function editform_output(&$event, $param){
69        // check if source view -> no captcha needed
70        if(!$param['oldhook']){
71            // get position of submit button
72            $pos = $event->data->findElementByAttribute('type','submit');
73            if(!$pos){
74                return; // no button -> source view mode
75            }
76        } elseif($param['editform'] && !$event->data['writable']){
77            if($param['editform'] && !$event->data['writable']){
78                return;
79            }
80        }
81
82        // If users don't need to fill in captcha then end this here.
83        if(!$this->getConf('forusers') && $_SERVER['REMOTE_USER']){
84            return;
85        }
86
87        $this->insert($event, $param);
88    }
89
90    /**
91     * insert html code for recaptcha into the form
92     *
93     * @param obj $event
94     * @param array $param
95     */
96    function insert(&$event, $param) {
97        global $conf;
98
99        $helper = plugin_load('helper','recaptcha');
100        $recaptcha = '<div style="width: 320px;"></div>';
101		// by default let's assume that protocol is http
102		$use_ssl = false;
103		// trying to find https in current url
104		if(preg_match('/^https:\/\/.*/', DOKU_URL)){
105			$use_ssl = true;
106		}
107        // see first if a language is defined for the plugin, if not try to use the language defined for dokuwiki
108        $lang = $this->getConf('lang') ? $this->getConf('lang') : (in_array($conf['lang'], $this->recaptchaLangs) ? $conf['lang'] : 'en');
109        $recaptcha .= "<script type='text/javascript'>
110            var RecaptchaOptions = {";
111        $recaptcha .= $this->getConf('theme') ? "theme: '".$this->getConf('theme')."'," : '';
112        $recaptcha .= "lang: '".$lang."'";
113        $recaptcha .= "
114    };
115    </script>";
116        $recaptcha .= $helper->getHTML($use_ssl);
117
118        if($param['oldhook']) {
119            echo $recaptcha;
120        } else {
121            $pos = $event->data->findElementByAttribute('type','submit');
122            $event->data->insertElement($pos++, $recaptcha);
123        }
124    }
125
126
127    /**
128     * process the answer to the captcha
129     *
130     * @param obj $event
131     * @param array $param
132     *
133     */
134    function preprocess(&$event, $param) {
135        // get and clean the action
136        $act = $this->_act_clean($event->data);
137        // If users don't need to fill in captcha then end this here.
138        if(!$this->getConf('forusers') && $_SERVER['REMOTE_USER']){
139            return;
140        }
141        // Check we are either registering or saving a html page
142        if(
143            ($act == 'register' && $this->getConf('regprotect') && $_POST['save'] ) ||
144            ($act == 'save' && $this->getConf('editprotect'))
145          ){
146
147            // Check the recaptcha answer and only submit if correct
148            $helper = plugin_load('helper', 'recaptcha');
149            $resp = $helper->check();
150
151            if (!$resp->is_valid) {
152                if($act == 'save'){
153                    // stay in preview mode
154                    msg($this->getLang('testfailed'),-1);
155                    $event->data = 'preview';
156                }else{
157                    // stay in register mode, but disable the save parameter
158                    msg($this->getLang('testfailed'),-1);
159                    $_POST['save']  = false;
160                }
161            }
162        }
163    }
164
165    /**
166     * Pre-Sanitize the action command
167     *
168     * Similar to act_clean in action.php but simplified and without
169     * error messages
170     * (taken from Andreas Gohrs captcha plugin)
171     */
172    function _act_clean($act){
173         // check if the action was given as array key
174         if(is_array($act)){
175           list($act) = array_keys($act);
176         }
177
178         //remove all bad chars
179         $act = strtolower($act);
180         $act = preg_replace('/[^a-z_]+/','',$act);
181
182         return $act;
183     }
184} //end of action class
185