+ function SimpleCaptcha() {
+ global $wgCaptchaStorageClass;
+ $this->storage = new $wgCaptchaStorageClass;
+ }
+
+ /**
+ * Insert a captcha prompt into the edit form.
+ * This sample implementation generates a simple arithmetic operation;
+ * it would be easy to defeat by machine.
+ *
+ * Override this!
+ *
+ * @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);
+
+ $index = $this->storeCaptcha( array( 'answer' => $answer ) );
+
+ return "<p><label for=\"wpCaptchaWord\">$test</label> = " .
+ wfElement( 'input', array(
+ 'name' => 'wpCaptchaWord',
+ 'id' => 'wpCaptchaWord',
+ 'tabindex' => 1 ) ) . // tab in before the edit textarea
+ "</p>\n" .
+ wfElement( 'input', array(
+ 'type' => 'hidden',
+ 'name' => 'wpCaptchaId',
+ 'id' => 'wpCaptchaId',
+ 'value' => $index ) );
+ }
+
+ /**
+ * Insert the captcha prompt into an edit form.
+ * @param OutputPage $out
+ */
+ function editCallback( &$out ) {
+ $out->addWikiText( $this->getMessage( $this->action ) );
+ $out->addHTML( $this->getForm() );
+ }
+
+ /**
+ * Show a message asking the user to enter a captcha on edit
+ * The result will be treated as wiki text
+ *
+ * @param $action Action being performed
+ * @return string
+ */
+ function getMessage( $action ) {
+ $name = 'captcha-' . $action;
+ $text = wfMsg( $name );
+ # Obtain a more tailored message, if possible, otherwise, fall back to
+ # the default for edits
+ return wfEmptyMsg( $name, $text ) ? wfMsg( 'captcha-edit' ) : $text;
+ }
+
+ /**
+ * Inject whazawhoo
+ * @fixme if multiple thingies insert a header, could break
+ * @param SimpleTemplate $template
+ * @return bool true to keep running callbacks
+ */
+ function injectUserCreate( &$template ) {
+ global $wgCaptchaTriggers, $wgOut;
+ if( $wgCaptchaTriggers['createaccount'] ) {
+ $template->set( 'header',
+ "<div class='captcha'>" .
+ $wgOut->parse( $this->getMessage( 'createaccount' ) ) .
+ $this->getForm() .
+ "</div>\n" );
+ }
+ return true;
+ }
+
+ /**
+ * Inject a captcha into the user login form after a failed
+ * password attempt as a speedbump for mass attacks.
+ * @fixme if multiple thingies insert a header, could break
+ * @param SimpleTemplate $template
+ * @return bool true to keep running callbacks
+ */
+ function injectUserLogin( &$template ) {
+ if( $this->isBadLoginTriggered() ) {
+ global $wgOut;
+ $template->set( 'header',
+ "<div class='captcha'>" .
+ $wgOut->parse( $this->getMessage( 'badlogin' ) ) .
+ $this->getForm() .
+ "</div>\n" );
+ }
+ return true;
+ }
+
+ /**
+ * When a bad login attempt is made, increment an expiring counter
+ * in the memcache cloud. Later checks for this may trigger a
+ * captcha display to prevent too many hits from the same place.
+ * @param User $user
+ * @param string $password
+ * @param int $retval authentication return value
+ * @return bool true to keep running callbacks
+ */
+ function triggerUserLogin( $user, $password, $retval ) {
+ global $wgCaptchaTriggers, $wgCaptchaBadLoginExpiration, $wgMemc;
+ if( $retval == LoginForm::WRONG_PASS && $wgCaptchaTriggers['badlogin'] ) {
+ $key = $this->badLoginKey();
+ $count = $wgMemc->get( $key );
+ if( !$count ) {
+ $wgMemc->add( $key, 0, $wgCaptchaBadLoginExpiration );
+ }
+ $count = $wgMemc->incr( $key );
+ }
+ return true;
+ }
+
+ /**
+ * Check if a bad login has already been registered for this
+ * IP address. If so, require a captcha.
+ * @return bool
+ * @access private
+ */
+ function isBadLoginTriggered() {
+ global $wgMemc;
+ return intval( $wgMemc->get( $this->badLoginKey() ) ) > 0;
+ }
+
+ /**
+ * Internal cache key for badlogin checks.
+ * @return string
+ * @access private
+ */
+ function badLoginKey() {
+ return wfMemcKey( 'captcha', 'badlogin', 'ip', wfGetIP() );
+ }
+
+ /**
+ * Check if the submitted form matches the captcha session data provided
+ * by the plugin when the form was generated.
+ *
+ * Override this!
+ *
+ * @param WebRequest $request
+ * @param array $info
+ * @return bool
+ */
+ function keyMatch( $request, $info ) {
+ return $request->getVal( 'wpCaptchaWord' ) == $info['answer'];
+ }
+
+ // ----------------------------------
+
+ /**
+ * @param EditPage $editPage
+ * @param string $action (edit/create/addurl...)
+ * @return bool true if action triggers captcha on editPage's namespace
+ */
+ function captchaTriggers( &$editPage, $action) {
+ global $wgCaptchaTriggers, $wgCaptchaTriggersOnNamespace;
+ //Special config for this NS?
+ if (isset( $wgCaptchaTriggersOnNamespace[$editPage->mTitle->getNamespace()][$action] ) )
+ return $wgCaptchaTriggersOnNamespace[$editPage->mTitle->getNamespace()][$action];
+
+ return ( !empty( $wgCaptchaTriggers[$action] ) ); //Default
+ }
+
+