(bug 13918), make CAPTCHA less obtrusive. Added a new global $wgCaptchaBadLoginAttemp...
[toast/cookiecaptcha.git] / ConfirmEdit_body.php
index 8e67910e920f35d2a86c1ecf50842233f0a81533..31635dac4323d4c7d2006320abb74bec78b5655f 100644 (file)
@@ -22,6 +22,10 @@ class ConfirmEditHooks {
        static function confirmEditMerged( &$editPage, $newtext ) {
                return self::getInstance()->confirmEditMerged( $editPage, $newtext );
        }
+       
+       static function confirmEditAPI( &$editPage, $newtext, &$resultArr ) {
+               return self::getInstance()->confirmEditAPI( $editPage, $newtext, $resultArr );
+       }
 
        static function injectUserCreate( &$template ) {
                return self::getInstance()->injectUserCreate( $template );
@@ -44,7 +48,7 @@ class ConfirmEditHooks {
        }
 }
 
-class CaptchaSpecialPage extends SpecialPage {
+class CaptchaSpecialPage extends UnlistedSpecialPage {
        function execute( $par ) {
                $this->setHeaders();
                $instance = ConfirmEditHooks::getInstance();
@@ -65,6 +69,25 @@ class SimpleCaptcha {
                $this->storage = new $wgCaptchaStorageClass;
        }
        
+       function getCaptcha() {
+               $a = mt_rand(0, 100);
+               $b = mt_rand(0, 10);
+               $op = mt_rand(0, 1) ? '+' : '-';
+
+               $test = "$a $op $b";
+               $answer = ($op == '+') ? ($a + $b) : ($a - $b);
+               return array('question' => $test, 'answer' => $answer);
+       }
+       
+       function addCaptchaAPI(&$resultArr) {
+               $captcha = $this->getCaptcha();
+               $index = $this->storeCaptcha( $captcha );
+               $resultArr['captcha']['type'] = 'simple';
+               $resultArr['captcha']['mime'] = 'text/plain';
+               $resultArr['captcha']['id'] = $index;
+               $resultArr['captcha']['question'] = $captcha['question'];
+       }
+       
        /**
         * Insert a captcha prompt into the edit form.
         * This sample implementation generates a simple arithmetic operation;
@@ -75,16 +98,10 @@ class SimpleCaptcha {
         * @return string HTML
         */
        function getForm() {
-               $a = mt_rand(0, 100);
-               $b = mt_rand(0, 10);
-               $op = mt_rand(0, 1) ? '+' : '-';
-
-               $test = "$a $op $b";
-               $answer = ($op == '+') ? ($a + $b) : ($a - $b);
+               $captcha = $this->getCaptcha();
+               $index = $this->storeCaptcha( $captcha );
 
-               $index = $this->storeCaptcha( array( 'answer' => $answer ) );
-
-               return "<p><label for=\"wpCaptchaWord\">$test</label> = " .
+               return "<p><label for=\"wpCaptchaWord\">{$captcha['question']}</label> = " .
                        wfElement( 'input', array(
                                'name' => 'wpCaptchaWord',
                                'id'   => 'wpCaptchaWord',
@@ -187,8 +204,8 @@ class SimpleCaptcha {
         * @access private
         */
        function isBadLoginTriggered() {
-               global $wgMemc;
-               return intval( $wgMemc->get( $this->badLoginKey() ) ) > 0;
+               global $wgMemc, $wgCaptchaBadLoginAttempts;
+               return intval( $wgMemc->get( $this->badLoginKey() ) ) > $wgCaptchaBadLoginAttempts;
        }
        
        /**
@@ -206,12 +223,12 @@ class SimpleCaptcha {
         *
         * Override this!
         *
-        * @param WebRequest $request
+        * @param string $answer
         * @param array $info
         * @return bool
         */
-       function keyMatch( $request, $info ) {
-               return $request->getVal( 'wpCaptchaWord' ) == $info['answer'];
+       function keyMatch( $answer, $info ) {
+               return $answer == $info['answer'];
        }
 
        // ----------------------------------
@@ -391,7 +408,7 @@ class SimpleCaptcha {
                        //$regex = 'http://+[a-z0-9_\-.]*(' . implode( '|', $lines ) . ')';
                        //return '/' . str_replace( '/', '\/', preg_replace('|\\\*/|', '/', $regex) ) . '/Si';
                        $regexes = '';
-                       $regexStart = '/http:\/\/+[a-z0-9_\-.]*(';
+                       $regexStart = '/^https?:\/\/+[a-z0-9_\-.]*(';
                        $regexEnd = ')/Si';
                        $regexMax = 4096;
                        $build = false;
@@ -430,21 +447,17 @@ class SimpleCaptcha {
                        $links[] = $row->el_to;
                }
                return $links;
-       }               
-
+       }
+       
        /**
-        * The main callback run on edit attempts.
-        * @param EditPage $editPage
-        * @param string $newtext
-        * @param string $section
-        * @param bool true to continue saving, false to abort and show a captcha form
+        * Backend function for confirmEdit() and confirmEditAPI()
+        * @return bool false if the CAPTCHA is rejected, true otherwise
         */
-       function confirmEdit( &$editPage, $newtext, $section, $merged = false ) {
+       private function doConfirmEdit( &$editPage, $newtext, $section, $merged = false ) {
                if( $this->shouldCheck( $editPage, $newtext, $section, $merged ) ) {
                        if( $this->passCaptcha() ) {
                                return true;
                        } else {
-                               $editPage->showEditForm( array( &$this, 'editCallback' ) );
                                return false;
                        }
                } else {
@@ -453,6 +466,28 @@ class SimpleCaptcha {
                }
        }
 
+       /**
+        * The main callback run on edit attempts.
+        * @param EditPage $editPage
+        * @param string $newtext
+        * @param string $section
+        * @param bool $merged
+        * @return bool true to continue saving, false to abort and show a captcha form
+        */
+       function confirmEdit( &$editPage, $newtext, $section, $merged = false ) {
+               global $wgTitle;
+               if( is_null( $wgTitle ) ) {
+                       # API mode
+                       # The CAPTCHA was already checked and approved 
+                       return true;
+               }
+               if( !$this->doConfirmEdit( $editPage, $newtext, $section, $merged ) ) {
+                       $editPage->showEditForm( array( &$this, 'editCallback' ) );
+                       return false;
+               }
+               return true;
+       }
+
        /**
         * A more efficient edit filter callback based on the text after section merging
         * @param EditPage $editPage
@@ -461,6 +496,15 @@ class SimpleCaptcha {
        function confirmEditMerged( &$editPage, $newtext ) {
                return $this->confirmEdit( $editPage, $newtext, false, true );
        }
+       
+       
+       function confirmEditAPI( &$editPage, $newtext, &$resultArr) {
+               if( !$this->doConfirmEdit( $editPage, $newtext, false, false ) ) {
+                       $this->addCaptchaAPI($resultArr);
+                       return false;
+               }
+               return true;
+       }
 
        /**
         * Hook for user creation form submissions.
@@ -508,7 +552,7 @@ class SimpleCaptcha {
                $info = $this->retrieveCaptcha();
                if( $info ) {
                        global $wgRequest;
-                       if( $this->keyMatch( $wgRequest, $info ) ) {
+                       if( $this->keyMatch( $wgRequest->getVal('wpCaptchaWord'), $info ) ) {
                                $this->log( "passed" );
                                $this->clearCaptcha( $info );
                                return true;