r27419 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r27418‎ | r27419 | r27420 >
Date:07:42, 12 November 2007
Author:tstarling
Status:old
Tags:
Comment:
* Optimised startup
* Use the new EditFilterMerged hook if available, for faster link finding
Modified paths:
  • /trunk/extensions/ConfirmEdit/ConfirmEdit.i18n.php (modified) (history)
  • /trunk/extensions/ConfirmEdit/ConfirmEdit.php (modified) (history)
  • /trunk/extensions/ConfirmEdit/ConfirmEdit_body.php (added) (history)
  • /trunk/extensions/ConfirmEdit/FancyCaptcha.class.php (added) (history)
  • /trunk/extensions/ConfirmEdit/FancyCaptcha.i18n.php (modified) (history)
  • /trunk/extensions/ConfirmEdit/FancyCaptcha.php (modified) (history)
  • /trunk/extensions/ConfirmEdit/MathCaptcha.class.php (added) (history)
  • /trunk/extensions/ConfirmEdit/MathCaptcha.php (modified) (history)

Diff [purge]

Index: trunk/extensions/ConfirmEdit/MathCaptcha.php
@@ -10,48 +10,9 @@
1111 * @licence GNU General Public Licence 2.0
1212 */
1313
14 -if( defined( 'MEDIAWIKI' ) ) {
15 -
16 - class MathCaptcha extends SimpleCaptcha {
17 -
18 - /** Validate a captcha response */
19 - function keyMatch( $req, $info ) {
20 - return (int)$req->getVal( 'wpCaptchaAnswer' ) == (int)$info['answer'];
21 - }
22 -
23 - /** Produce a nice little form */
24 - function getForm() {
25 - list( $sum, $answer ) = $this->pickSum();
26 - $index = $this->storeCaptcha( array( 'answer' => $answer ) );
27 -
28 - $form = '<table><tr><td>' . $this->fetchMath( $sum ) . '</td>';
29 - $form .= '<td>' . wfInput( 'wpCaptchaAnswer', false, false, array( 'tabindex' => '1' ) ) . '</td></tr></table>';
30 - $form .= wfHidden( 'wpCaptchaId', $index );
31 - return $form;
32 - }
33 -
34 - /** Pick a random sum */
35 - function pickSum() {
36 - $a = mt_rand( 0, 100 );
37 - $b = mt_rand( 0, 10 );
38 - $op = mt_rand( 0, 1 ) ? '+' : '-';
39 - $sum = "{$a} {$op} {$b} = ";
40 - $ans = $op == '+' ? ( $a + $b ) : ( $a - $b );
41 - return array( $sum, $ans );
42 - }
43 -
44 - /** Fetch the math */
45 - function fetchMath( $sum ) {
46 - $math = new MathRenderer( $sum );
47 - $math->setOutputMode( MW_MATH_PNG );
48 - $html = $math->render();
49 - return preg_replace( '/alt=".*"/', '', $html );
50 - }
51 -
52 - }
53 -
54 -} else {
 14+if( !defined( 'MEDIAWIKI' ) ) {
5515 echo( "This file is an extension to the MediaWiki software and cannot be used standalone.\n" );
5616 die( 1 );
5717 }
 18+$wgAutoloadClasses['MathCaptcha'] = dirname( __FILE__ ) . '/MathCaptcha.class.php';
5819
Index: trunk/extensions/ConfirmEdit/ConfirmEdit_body.php
@@ -0,0 +1,670 @@
 2+<?php
 3+
 4+class ConfirmEditHooks {
 5+ static function getInstance() {
 6+ global $wgCaptcha, $wgCaptchaClass, $wgExtensionMessagesFiles;
 7+ static $done = false;
 8+ if ( !$done ) {
 9+ $done = true;
 10+ wfLoadExtensionMessages( 'ConfirmEdit' );
 11+ if ( isset( $wgExtensionMessagesFiles[$wgCaptchaClass] ) ) {
 12+ wfLoadExtensionMessages( $wgCaptchaClass );
 13+ }
 14+ $wgCaptcha = new $wgCaptchaClass;
 15+ }
 16+ return $wgCaptcha;
 17+ }
 18+
 19+ static function confirmEdit( &$editPage, $newtext, $section ) {
 20+ return self::getInstance()->confirmEdit( $editPage, $newtext, $section );
 21+ }
 22+
 23+ static function confirmEditMerged( &$editPage, $newtext ) {
 24+ return self::getInstance()->confirmEditMerged( $editPage, $newtext );
 25+ }
 26+
 27+ static function injectUserCreate( &$template ) {
 28+ return self::getInstance()->injectUserCreate( $template );
 29+ }
 30+
 31+ static function confirmUserCreate( $u, &$message ) {
 32+ return self::getInstance()->confirmUserCreate( $u, $message );
 33+ }
 34+
 35+ static function triggerUserLogin( $user, $password, $retval ) {
 36+ return self::getInstance()->triggerUserLogin( $user, $password, $retval );
 37+ }
 38+
 39+ static function injectUserLogin( &$template ) {
 40+ return self::getInstance()->injectUserLogin( $template );
 41+ }
 42+
 43+ static function confirmUserLogin( $u, $pass, &$retval ) {
 44+ return self::getInstance()->confirmUserLogin( $u, $pass, $retval );
 45+ }
 46+}
 47+
 48+class CaptchaSpecialPage extends SpecialPage {
 49+ function execute( $par ) {
 50+ $this->setHeaders();
 51+ $instance = ConfirmEditHooks::getInstance();
 52+ switch( $par ) {
 53+ case "image":
 54+ return $instance->showImage();
 55+ case "help":
 56+ default:
 57+ return $instance->showHelp();
 58+ }
 59+ }
 60+}
 61+
 62+
 63+class SimpleCaptcha {
 64+ function SimpleCaptcha() {
 65+ global $wgCaptchaStorageClass;
 66+ $this->storage = new $wgCaptchaStorageClass;
 67+ }
 68+
 69+ /**
 70+ * Insert a captcha prompt into the edit form.
 71+ * This sample implementation generates a simple arithmetic operation;
 72+ * it would be easy to defeat by machine.
 73+ *
 74+ * Override this!
 75+ *
 76+ * @return string HTML
 77+ */
 78+ function getForm() {
 79+ $a = mt_rand(0, 100);
 80+ $b = mt_rand(0, 10);
 81+ $op = mt_rand(0, 1) ? '+' : '-';
 82+
 83+ $test = "$a $op $b";
 84+ $answer = ($op == '+') ? ($a + $b) : ($a - $b);
 85+
 86+ $index = $this->storeCaptcha( array( 'answer' => $answer ) );
 87+
 88+ return "<p><label for=\"wpCaptchaWord\">$test</label> = " .
 89+ wfElement( 'input', array(
 90+ 'name' => 'wpCaptchaWord',
 91+ 'id' => 'wpCaptchaWord',
 92+ 'tabindex' => 1 ) ) . // tab in before the edit textarea
 93+ "</p>\n" .
 94+ wfElement( 'input', array(
 95+ 'type' => 'hidden',
 96+ 'name' => 'wpCaptchaId',
 97+ 'id' => 'wpCaptchaId',
 98+ 'value' => $index ) );
 99+ }
 100+
 101+ /**
 102+ * Insert the captcha prompt into an edit form.
 103+ * @param OutputPage $out
 104+ */
 105+ function editCallback( &$out ) {
 106+ $out->addWikiText( $this->getMessage( $this->action ) );
 107+ $out->addHTML( $this->getForm() );
 108+ }
 109+
 110+ /**
 111+ * Show a message asking the user to enter a captcha on edit
 112+ * The result will be treated as wiki text
 113+ *
 114+ * @param $action Action being performed
 115+ * @return string
 116+ */
 117+ function getMessage( $action ) {
 118+ $name = 'captcha-' . $action;
 119+ $text = wfMsg( $name );
 120+ # Obtain a more tailored message, if possible, otherwise, fall back to
 121+ # the default for edits
 122+ return wfEmptyMsg( $name, $text ) ? wfMsg( 'captcha-edit' ) : $text;
 123+ }
 124+
 125+ /**
 126+ * Inject whazawhoo
 127+ * @fixme if multiple thingies insert a header, could break
 128+ * @param SimpleTemplate $template
 129+ * @return bool true to keep running callbacks
 130+ */
 131+ function injectUserCreate( &$template ) {
 132+ global $wgCaptchaTriggers, $wgOut;
 133+ if( $wgCaptchaTriggers['createaccount'] ) {
 134+ $template->set( 'header',
 135+ "<div class='captcha'>" .
 136+ $wgOut->parse( $this->getMessage( 'createaccount' ) ) .
 137+ $this->getForm() .
 138+ "</div>\n" );
 139+ }
 140+ return true;
 141+ }
 142+
 143+ /**
 144+ * Inject a captcha into the user login form after a failed
 145+ * password attempt as a speedbump for mass attacks.
 146+ * @fixme if multiple thingies insert a header, could break
 147+ * @param SimpleTemplate $template
 148+ * @return bool true to keep running callbacks
 149+ */
 150+ function injectUserLogin( &$template ) {
 151+ if( $this->isBadLoginTriggered() ) {
 152+ global $wgOut;
 153+ $template->set( 'header',
 154+ "<div class='captcha'>" .
 155+ $wgOut->parse( $this->getMessage( 'badlogin' ) ) .
 156+ $this->getForm() .
 157+ "</div>\n" );
 158+ }
 159+ return true;
 160+ }
 161+
 162+ /**
 163+ * When a bad login attempt is made, increment an expiring counter
 164+ * in the memcache cloud. Later checks for this may trigger a
 165+ * captcha display to prevent too many hits from the same place.
 166+ * @param User $user
 167+ * @param string $password
 168+ * @param int $retval authentication return value
 169+ * @return bool true to keep running callbacks
 170+ */
 171+ function triggerUserLogin( $user, $password, $retval ) {
 172+ global $wgCaptchaTriggers, $wgCaptchaBadLoginExpiration, $wgMemc;
 173+ if( $retval == LoginForm::WRONG_PASS && $wgCaptchaTriggers['badlogin'] ) {
 174+ $key = $this->badLoginKey();
 175+ $count = $wgMemc->get( $key );
 176+ if( !$count ) {
 177+ $wgMemc->add( $key, 0, $wgCaptchaBadLoginExpiration );
 178+ }
 179+ $count = $wgMemc->incr( $key );
 180+ }
 181+ return true;
 182+ }
 183+
 184+ /**
 185+ * Check if a bad login has already been registered for this
 186+ * IP address. If so, require a captcha.
 187+ * @return bool
 188+ * @access private
 189+ */
 190+ function isBadLoginTriggered() {
 191+ global $wgMemc;
 192+ return intval( $wgMemc->get( $this->badLoginKey() ) ) > 0;
 193+ }
 194+
 195+ /**
 196+ * Internal cache key for badlogin checks.
 197+ * @return string
 198+ * @access private
 199+ */
 200+ function badLoginKey() {
 201+ return wfMemcKey( 'captcha', 'badlogin', 'ip', wfGetIP() );
 202+ }
 203+
 204+ /**
 205+ * Check if the submitted form matches the captcha session data provided
 206+ * by the plugin when the form was generated.
 207+ *
 208+ * Override this!
 209+ *
 210+ * @param WebRequest $request
 211+ * @param array $info
 212+ * @return bool
 213+ */
 214+ function keyMatch( $request, $info ) {
 215+ return $request->getVal( 'wpCaptchaWord' ) == $info['answer'];
 216+ }
 217+
 218+ // ----------------------------------
 219+
 220+ /**
 221+ * @param EditPage $editPage
 222+ * @param string $action (edit/create/addurl...)
 223+ * @return bool true if action triggers captcha on editPage's namespace
 224+ */
 225+ function captchaTriggers( &$editPage, $action) {
 226+ global $wgCaptchaTriggers, $wgCaptchaTriggersOnNamespace;
 227+ //Special config for this NS?
 228+ if (isset( $wgCaptchaTriggersOnNamespace[$editPage->mTitle->getNamespace()][$action] ) )
 229+ return $wgCaptchaTriggersOnNamespace[$editPage->mTitle->getNamespace()][$action];
 230+
 231+ return ( !empty( $wgCaptchaTriggers[$action] ) ); //Default
 232+ }
 233+
 234+
 235+ /**
 236+ * @param EditPage $editPage
 237+ * @param string $newtext
 238+ * @param string $section
 239+ * @return bool true if the captcha should run
 240+ */
 241+ function shouldCheck( &$editPage, $newtext, $section, $merged = false ) {
 242+ $this->trigger = '';
 243+ $title = $editPage->mArticle->getTitle();
 244+
 245+ global $wgUser;
 246+ if( $wgUser->isAllowed( 'skipcaptcha' ) ) {
 247+ wfDebug( "ConfirmEdit: user group allows skipping captcha\n" );
 248+ return false;
 249+ }
 250+ global $wgCaptchaWhitelistIP;
 251+ if( !empty( $wgCaptchaWhitelistIP ) ) {
 252+ $ip = wfGetIp();
 253+ foreach ( $wgCaptchaWhitelistIP as $range ) {
 254+ if ( IP::isInRange( $ip, $range ) ) {
 255+ return false;
 256+ }
 257+ }
 258+ }
 259+
 260+
 261+ global $wgEmailAuthentication, $ceAllowConfirmedEmail;
 262+ if( $wgEmailAuthentication && $ceAllowConfirmedEmail &&
 263+ $wgUser->isEmailConfirmed() ) {
 264+ wfDebug( "ConfirmEdit: user has confirmed mail, skipping captcha\n" );
 265+ return false;
 266+ }
 267+
 268+ if( $this->captchaTriggers( $editPage, 'edit' ) ) {
 269+ // Check on all edits
 270+ global $wgUser;
 271+ $this->trigger = sprintf( "edit trigger by '%s' at [[%s]]",
 272+ $wgUser->getName(),
 273+ $title->getPrefixedText() );
 274+ $this->action = 'edit';
 275+ wfDebug( "ConfirmEdit: checking all edits...\n" );
 276+ return true;
 277+ }
 278+
 279+ if( $this->captchaTriggers( $editPage, 'create' ) && !$editPage->mTitle->exists() ) {
 280+ //Check if creating a page
 281+ global $wgUser;
 282+ $this->trigger = sprintf( "Create trigger by '%s' at [[%s]]",
 283+ $wgUser->getName(),
 284+ $title->getPrefixedText() );
 285+ $this->action = 'create';
 286+ wfDebug( "ConfirmEdit: checking on page creation...\n" );
 287+ return true;
 288+ }
 289+
 290+ if( $this->captchaTriggers( $editPage, 'addurl' ) ) {
 291+ // Only check edits that add URLs
 292+ if ( $merged ) {
 293+ // Get links from the database
 294+ $oldLinks = $this->getLinksFromTracker( $title );
 295+ // Share a parse operation with Article::doEdit()
 296+ $editInfo = $editPage->mArticle->prepareTextForEdit( $newtext );
 297+ $newLinks = array_keys( $editInfo->output->getExternalLinks() );
 298+ } else {
 299+ // Get link changes in the slowest way known to man
 300+ $oldtext = $this->loadText( $editPage, $section );
 301+ $oldLinks = $this->findLinks( $oldtext );
 302+ $newLinks = $this->findLinks( $newtext );
 303+ }
 304+
 305+ $unknownLinks = array_filter( $newLinks, array( &$this, 'filterLink' ) );
 306+ $addedLinks = array_diff( $unknownLinks, $oldLinks );
 307+ $numLinks = count( $addedLinks );
 308+
 309+ if( $numLinks > 0 ) {
 310+ global $wgUser;
 311+ $this->trigger = sprintf( "%dx url trigger by '%s' at [[%s]]: %s",
 312+ $numLinks,
 313+ $wgUser->getName(),
 314+ $title->getPrefixedText(),
 315+ implode( ", ", $addedLinks ) );
 316+ $this->action = 'addurl';
 317+ return true;
 318+ }
 319+ }
 320+
 321+ global $wgCaptchaRegexes;
 322+ if( !empty( $wgCaptchaRegexes ) ) {
 323+ // Custom regex checks
 324+ $oldtext = $this->loadText( $editPage, $section );
 325+
 326+ foreach( $wgCaptchaRegexes as $regex ) {
 327+ $newMatches = array();
 328+ if( preg_match_all( $regex, $newtext, $newMatches ) ) {
 329+ $oldMatches = array();
 330+ preg_match_all( $regex, $oldtext, $oldMatches );
 331+
 332+ $addedMatches = array_diff( $newMatches[0], $oldMatches[0] );
 333+
 334+ $numHits = count( $addedMatches );
 335+ if( $numHits > 0 ) {
 336+ global $wgUser;
 337+ $this->trigger = sprintf( "%dx %s at [[%s]]: %s",
 338+ $numHits,
 339+ $regex,
 340+ $wgUser->getName(),
 341+ $title->getPrefixedText(),
 342+ implode( ", ", $addedMatches ) );
 343+ $this->action = 'edit';
 344+ return true;
 345+ }
 346+ }
 347+ }
 348+ }
 349+
 350+ return false;
 351+ }
 352+
 353+ /**
 354+ * Filter callback function for URL whitelisting
 355+ * @param string url to check
 356+ * @return bool true if unknown, false if whitelisted
 357+ * @access private
 358+ */
 359+ function filterLink( $url ) {
 360+ global $wgCaptchaWhitelist;
 361+ $source = wfMsgForContent( 'captcha-addurl-whitelist' );
 362+
 363+ $whitelist = wfEmptyMsg( 'captcha-addurl-whitelist', $source )
 364+ ? false
 365+ : $this->buildRegexes( explode( "\n", $source ) );
 366+
 367+ $cwl = $wgCaptchaWhitelist !== false ? preg_match( $wgCaptchaWhitelist, $url ) : false;
 368+ $wl = $whitelist !== false ? preg_match( $whitelist, $url ) : false;
 369+
 370+ return !( $cwl || $wl );
 371+ }
 372+
 373+ /**
 374+ * Build regex from whitelist
 375+ * @param string lines from [[MediaWiki:Captcha-addurl-whitelist]]
 376+ * @return string Regex or bool false if whitelist is empty
 377+ * @access private
 378+ */
 379+ function buildRegexes( $lines ) {
 380+ # Code duplicated from the SpamBlacklist extension (r19197)
 381+
 382+ # Strip comments and whitespace, then remove blanks
 383+ $lines = array_filter( array_map( 'trim', preg_replace( '/#.*$/', '', $lines ) ) );
 384+
 385+ # No lines, don't make a regex which will match everything
 386+ if ( count( $lines ) == 0 ) {
 387+ wfDebug( "No lines\n" );
 388+ return false;
 389+ } else {
 390+ # Make regex
 391+ # It's faster using the S modifier even though it will usually only be run once
 392+ //$regex = 'http://+[a-z0-9_\-.]*(' . implode( '|', $lines ) . ')';
 393+ //return '/' . str_replace( '/', '\/', preg_replace('|\\\*/|', '/', $regex) ) . '/Si';
 394+ $regexes = '';
 395+ $regexStart = '/http:\/\/+[a-z0-9_\-.]*(';
 396+ $regexEnd = ')/Si';
 397+ $regexMax = 4096;
 398+ $build = false;
 399+ foreach( $lines as $line ) {
 400+ // FIXME: not very robust size check, but should work. :)
 401+ if( $build === false ) {
 402+ $build = $line;
 403+ } elseif( strlen( $build ) + strlen( $line ) > $regexMax ) {
 404+ $regexes .= $regexStart .
 405+ str_replace( '/', '\/', preg_replace('|\\\*/|', '/', $build) ) .
 406+ $regexEnd;
 407+ $build = $line;
 408+ } else {
 409+ $build .= '|' . $line;
 410+ }
 411+ }
 412+ if( $build !== false ) {
 413+ $regexes .= $regexStart .
 414+ str_replace( '/', '\/', preg_replace('|\\\*/|', '/', $build) ) .
 415+ $regexEnd;
 416+ }
 417+ return $regexes;
 418+ }
 419+ }
 420+
 421+ /**
 422+ * Load external links from the externallinks table
 423+ */
 424+ function getLinksFromTracker( $title ) {
 425+ $dbr =& wfGetDB( DB_SLAVE );
 426+ $id = $title->getArticleId(); // should be zero queries
 427+ $res = $dbr->select( 'externallinks', array( 'el_to' ),
 428+ array( 'el_from' => $id ), __METHOD__ );
 429+ $links = array();
 430+ while ( $row = $dbr->fetchObject( $res ) ) {
 431+ $links[] = $row->el_to;
 432+ }
 433+ return $links;
 434+ }
 435+
 436+ /**
 437+ * The main callback run on edit attempts.
 438+ * @param EditPage $editPage
 439+ * @param string $newtext
 440+ * @param string $section
 441+ * @param bool true to continue saving, false to abort and show a captcha form
 442+ */
 443+ function confirmEdit( &$editPage, $newtext, $section, $merged = false ) {
 444+ if( $this->shouldCheck( $editPage, $newtext, $section, $merged ) ) {
 445+ if( $this->passCaptcha() ) {
 446+ return true;
 447+ } else {
 448+ $editPage->showEditForm( array( &$this, 'editCallback' ) );
 449+ return false;
 450+ }
 451+ } else {
 452+ wfDebug( "ConfirmEdit: no need to show captcha.\n" );
 453+ return true;
 454+ }
 455+ }
 456+
 457+ /**
 458+ * A more efficient edit filter callback based on the text after section merging
 459+ * @param EditPage $editPage
 460+ * @param string $newtext
 461+ */
 462+ function confirmEditMerged( &$editPage, $newtext ) {
 463+ return $this->confirmEdit( $editPage, $newtext, false, true );
 464+ }
 465+
 466+ /**
 467+ * Hook for user creation form submissions.
 468+ * @param User $u
 469+ * @param string $message
 470+ * @return bool true to continue, false to abort user creation
 471+ */
 472+ function confirmUserCreate( $u, &$message ) {
 473+ global $wgCaptchaTriggers;
 474+ if( $wgCaptchaTriggers['createaccount'] ) {
 475+ $this->trigger = "new account '" . $u->getName() . "'";
 476+ if( !$this->passCaptcha() ) {
 477+ $message = wfMsg( 'captcha-createaccount-fail' );
 478+ return false;
 479+ }
 480+ }
 481+ return true;
 482+ }
 483+
 484+ /**
 485+ * Hook for user login form submissions.
 486+ * @param User $u
 487+ * @param string $message
 488+ * @return bool true to continue, false to abort user creation
 489+ */
 490+ function confirmUserLogin( $u, $pass, &$retval ) {
 491+ if( $this->isBadLoginTriggered() ) {
 492+ $this->trigger = "post-badlogin login '" . $u->getName() . "'";
 493+ if( !$this->passCaptcha() ) {
 494+ $message = wfMsg( 'captcha-badlogin-fail' );
 495+ // Emulate a bad-password return to confuse the shit out of attackers
 496+ $retval = LoginForm::WRONG_PASS;
 497+ return false;
 498+ }
 499+ }
 500+ return true;
 501+ }
 502+
 503+ /**
 504+ * Given a required captcha run, test form input for correct
 505+ * input on the open session.
 506+ * @return bool if passed, false if failed or new session
 507+ */
 508+ function passCaptcha() {
 509+ $info = $this->retrieveCaptcha();
 510+ if( $info ) {
 511+ global $wgRequest;
 512+ if( $this->keyMatch( $wgRequest, $info ) ) {
 513+ $this->log( "passed" );
 514+ $this->clearCaptcha( $info );
 515+ return true;
 516+ } else {
 517+ $this->clearCaptcha( $info );
 518+ $this->log( "bad form input" );
 519+ return false;
 520+ }
 521+ } else {
 522+ $this->log( "new captcha session" );
 523+ return false;
 524+ }
 525+ }
 526+
 527+ /**
 528+ * Log the status and any triggering info for debugging or statistics
 529+ * @param string $message
 530+ */
 531+ function log( $message ) {
 532+ wfDebugLog( 'captcha', 'ConfirmEdit: ' . $message . '; ' . $this->trigger );
 533+ }
 534+
 535+ /**
 536+ * Generate a captcha session ID and save the info in PHP's session storage.
 537+ * (Requires the user to have cookies enabled to get through the captcha.)
 538+ *
 539+ * A random ID is used so legit users can make edits in multiple tabs or
 540+ * windows without being unnecessarily hobbled by a serial order requirement.
 541+ * Pass the returned id value into the edit form as wpCaptchaId.
 542+ *
 543+ * @param array $info data to store
 544+ * @return string captcha ID key
 545+ */
 546+ function storeCaptcha( $info ) {
 547+ if( !isset( $info['index'] ) ) {
 548+ // Assign random index if we're not udpating
 549+ $info['index'] = strval( mt_rand() );
 550+ }
 551+ $this->storage->store( $info['index'], $info );
 552+ return $info['index'];
 553+ }
 554+
 555+ /**
 556+ * Fetch this session's captcha info.
 557+ * @return mixed array of info, or false if missing
 558+ */
 559+ function retrieveCaptcha() {
 560+ global $wgRequest;
 561+ $index = $wgRequest->getVal( 'wpCaptchaId' );
 562+ return $this->storage->retrieve( $index );
 563+ }
 564+
 565+ /**
 566+ * Clear out existing captcha info from the session, to ensure
 567+ * it can't be reused.
 568+ */
 569+ function clearCaptcha( $info ) {
 570+ $this->storage->clear( $info['index'] );
 571+ }
 572+
 573+ /**
 574+ * Retrieve the current version of the page or section being edited...
 575+ * @param EditPage $editPage
 576+ * @param string $section
 577+ * @return string
 578+ * @access private
 579+ */
 580+ function loadText( $editPage, $section ) {
 581+ $rev = Revision::newFromTitle( $editPage->mTitle );
 582+ if( is_null( $rev ) ) {
 583+ return "";
 584+ } else {
 585+ $text = $rev->getText();
 586+ if( $section != '' ) {
 587+ return Article::getSection( $text, $section );
 588+ } else {
 589+ return $text;
 590+ }
 591+ }
 592+ }
 593+
 594+ /**
 595+ * Extract a list of all recognized HTTP links in the text.
 596+ * @param string $text
 597+ * @return array of strings
 598+ */
 599+ function findLinks( $text ) {
 600+ global $wgParser, $wgTitle, $wgUser;
 601+
 602+ $options = new ParserOptions();
 603+ $text = $wgParser->preSaveTransform( $text, $wgTitle, $wgUser, $options );
 604+ $out = $wgParser->parse( $text, $wgTitle, $options );
 605+
 606+ return array_keys( $out->getExternalLinks() );
 607+ }
 608+
 609+ /**
 610+ * Show a page explaining what this wacky thing is.
 611+ */
 612+ function showHelp() {
 613+ global $wgOut, $ceAllowConfirmedEmail;
 614+ $wgOut->setPageTitle( wfMsg( 'captchahelp-title' ) );
 615+ $wgOut->addWikiText( wfMsg( 'captchahelp-text' ) );
 616+ if ( $this->storage->cookiesNeeded() ) {
 617+ $wgOut->addWikiText( wfMsg( 'captchahelp-cookies-needed' ) );
 618+ }
 619+ }
 620+
 621+}
 622+
 623+class CaptchaSessionStore {
 624+ function store( $index, $info ) {
 625+ $_SESSION['captcha' . $info['index']] = $info;
 626+ }
 627+
 628+ function retrieve( $index ) {
 629+ if( isset( $_SESSION['captcha' . $index] ) ) {
 630+ return $_SESSION['captcha' . $index];
 631+ } else {
 632+ return false;
 633+ }
 634+ }
 635+
 636+ function clear( $index ) {
 637+ unset( $_SESSION['captcha' . $index] );
 638+ }
 639+
 640+ function cookiesNeeded() {
 641+ return true;
 642+ }
 643+}
 644+
 645+class CaptchaCacheStore {
 646+ function store( $index, $info ) {
 647+ global $wgMemc, $wgCaptchaSessionExpiration;
 648+ $wgMemc->set( wfMemcKey( 'captcha', $index ), $info,
 649+ $wgCaptchaSessionExpiration );
 650+ }
 651+
 652+ function retrieve( $index ) {
 653+ global $wgMemc;
 654+ $info = $wgMemc->get( wfMemcKey( 'captcha', $index ) );
 655+ if( $info ) {
 656+ return $info;
 657+ } else {
 658+ return false;
 659+ }
 660+ }
 661+
 662+ function clear( $index ) {
 663+ global $wgMemc;
 664+ $wgMemc->delete( wfMemcKey( 'captcha', $index ) );
 665+ }
 666+
 667+ function cookiesNeeded() {
 668+ return false;
 669+ }
 670+}
 671+
Property changes on: trunk/extensions/ConfirmEdit/ConfirmEdit_body.php
___________________________________________________________________
Added: svn:eol-style
1672 + native
Index: trunk/extensions/ConfirmEdit/ConfirmEdit.i18n.php
@@ -5,9 +5,9 @@
66 * @addtogroup Extensions
77 */
88
9 -$wgConfirmEditMessages = array();
 9+$messages = array();
1010
11 -$wgConfirmEditMessages['en'] = array(
 11+$messages['en'] = array(
1212 'captcha-edit' => 'To edit this article, please solve the simple sum below and enter the answer in
1313 the box ([[Special:Captcha/help|more info]]):',
1414 'captcha-addurl' => 'Your edit includes new external links. To help protect against automated
@@ -36,7 +36,7 @@
3737 #</pre> <!-- leave this line exactly as it is -->',
3838 );
3939
40 -$wgConfirmEditMessages['af'] = array(
 40+$messages['af'] = array(
4141 'captcha-edit' => "U wysiging bevat nuwe webskakels. Neem kennis dat blote reklame van u werf, produk of besigheid as vandalisme beskou kan word. As beskerming teen outomatiese gemorsbydraes, sal u die woorde wat onder verskyn in die prentjie moet intik: <br />([[Spesiaal:Captcha/help|Wat is hierdie?]])",
4242 'captcha-addurl' => "U wysiging bevat nuwe webskakels. Neem kennis dat blote reklame van u werf, produk of besigheid as vandalisme beskou kan word. As beskerming teen outomatiese gemorsbydraes, sal u die woorde wat onder verskyn in die prentjie moet intik: <br />([[Spesiaal:Captcha/help|Wat is hierdie?]])",
4343 'captcha-create' => "U wysiging bevat nuwe webskakels. Neem kennis dat blote reklame van u werf, produk of besigheid as vandalisme beskou kan word. As beskerming teen outomatiese gemorsbydraes, sal u die woorde wat onder verskyn in die prentjie moet intik: <br />([[Spesiaal:Captcha/help|Wat is hierdie?]])",
@@ -45,10 +45,10 @@
4646 'captcha-createaccount' => "As 'n beskerming teen geoutomatiseerde gemors, tik asseblief die woorde wat in die beeld verskyn in om 'n rekening te skep: <br />([[Special:Captcha/help|Wat is hierdie?]])",
4747 'captcha-createaccount-fail' => "Verkeerde of geen bevestigingkode.",
4848 );
49 -$wgConfirmEditMessages['am'] = array(
 49+$messages['am'] = array(
5050 'captcha-createaccount' => 'ያልተፈለገ የመኪናነት አባልነት ለመከላከል፥ አባል ለመሆን በዚህ ምስል የታዩት እንግሊዝኛ ቃላት ወይም ቁጥር መልስ በትክክል መጻፍ ግዴታ ነው። ([[Special:Captcha/help|ይህ ምንድነው?]]):',
5151 );
52 -$wgConfirmEditMessages['ar'] = array(
 52+$messages['ar'] = array(
5353 'captcha-edit' => 'يحتوي تعديلك هذا على وصلات خارجية. للحماية من السخام الأوتوماتيكي، قم من فضلك بحل المسألة الرياضية البسيطة أدناه وأدخل الحل في الصندوق ([[Special:Captcha/help|مزيد من المعلومات]]):',
5454 'captcha-addurl' => 'تعديلك يحتوي على وصلات خارجية جديدة. للمساعدة في الحماية من السخام الأوتوماتيكي، من فضلك حل عملية الجمع بالأسفل و أضف الحل في الصندوق ([[Special::Captcha/help|معلومات إضافية]]):',
5555 'captcha-badlogin' => 'للمساعدة في الحماية ضد سرقة كلمات السر، من فضلك حل عملية الجمع البسيطة بالأسفل و أدخل الحل في الصندوق ([[Special:Captcha/help|معلومات إضافية]]):',
@@ -71,11 +71,11 @@
7272 # * كل سطر غير فارغ هو قطعة ريجيكس والتي توافق فقط المعيلين داخل المسارات
7373 #</pre> <!-- leave this line exactly as it is -->',
7474 );
75 -$wgConfirmEditMessages['bcl'] = array(
 75+$messages['bcl'] = array(
7676 'captcha-create' => 'Tangarig maggibo an pahina, paki simbagan an simpleng suma sa ibaba asin ikaag an simbag sa laog kan kahon ([[Special:Captcha/help|more info]]):',
7777 'captchahelp-title' => 'Tabang sa Captcha',
7878 );
79 -$wgConfirmEditMessages['br'] = array(
 79+$messages['br'] = array(
8080 'captcha-edit' => 'Liammoù diavaez nevez zo bet ouzhpennet ganeoc\'h. A-benn en em wareziñ diouzh ar spam emgefre skrivit disoc\'h ar jedadennig eeun-mañ er stern : <br />([[Special:Captcha/help|Petra eo se?]])',
8181 'captcha-addurl' => 'Liammoù diavaez nevez zo bet ouzhpennet ganeoc\'h. A-benn en em wareziñ diouzh ar spam emgefre skrivit disoc\'h ar jedadennig eeun-mañ er stern : <br />([[Special:Captcha/help|Petra eo se?]])',
8282 'captcha-create' => 'Liammoù diavaez nevez zo bet ouzhpennet ganeoc\'h. A-benn en em wareziñ diouzh ar spam emgefre skrivit disoc\'h ar jedadennig eeun-mañ er stern : <br />([[Special:Captcha/help|Petra eo se?]])',
@@ -84,7 +84,7 @@
8585 'captchahelp-title' => 'Skoazell Capcha',
8686 'captchahelp-text' => 'Alies e vez taget al lec\'hiennoù a zegemer kemennadennoù a-berzh an holl, evel ar wiki-mañ, gant ar spamerien a implij ostilhoù emgefre evit postañ o liammoù war lec\'hiennoù a bep seurt. Diverket e c\'hallont bezañ, gwir eo, kazus-mat ez int memes tra. A-wechoù, dreist-holl pa vez ouzhpennet liammoù Web nevez war ur bajenn, e c\'hallo ar wiki-mañ diskouez deoc\'h ur skeudenn warni un tamm testenn liv pe a-dreuz. Goulennet e vo diganeoc\'h skrivañ ar gerioù deuet war wel. Un trevell start da emgefrekaat eo hemañ. Gant se e c\'hallo an implijerien wirion postañ ar pezh a fel ldezho tra ma vo lakaet un harz d\'an darn vrasañ eus ar spamerien pe d\'an dagerien robotek all. Koulskoude e c\'hallo an implijerien berr o gweled pe ar re a implij merdeerioù diazezet war ar skrid pe war ar vouezh bezañ strafuilhet gant se. N\'omp ket evit kinnig un diskoulm dre glevet evit c\'hoazh. Kit e darempred gant merourien al lec\'hienn m\'hoc\'h eus diaesterioù evit kemer perzh abalamour d\'an teknik-se. Pouezit war bouton \'kent\' ho merdeer evit distreiñ d\'ar bajenn gemmañ.',
8787 );
88 -$wgConfirmEditMessages['bs'] = array(
 88+$messages['bs'] = array(
8989 'captcha-edit' => 'Vaša izmjena uključuje nove URL poveznice; kao zaštita od automatizovanog vandalizma, moraćete da ukucate riječi koje su prikazane u slici:
9090 <br />([[{{ns:special}}:Captcha/help|Šta je ovo?]])',
9191 'captcha-addurl' => 'Vaša izmjena uključuje nove URL poveznice; kao zaštita od automatizovanog vandalizma, moraćete da ukucate riječi koje su prikazane u slici:
@@ -102,7 +102,7 @@
103103 <br />([[{{ns:special}}:Captcha/help|Šta je ovo?]])',
104104 'captcha-createaccount-fail' => 'Netačan unos ili nedostatak šifre za potvrđivanje.',
105105 );
106 -$wgConfirmEditMessages['ca'] = array(
 106+$messages['ca'] = array(
107107 'captcha-edit' => 'Per a poder editar aquest article cal que resolgueu aquesta simple suma i introduïu el resultat en el quadre ([[Special:Captcha/help|més informació]]):',
108108 'captcha-addurl' => 'La vostra edició conté enllaços externs nous. Com a protecció contra la brossa de propaganda automàtica, cal que resolgueu aquesta simple suma i introduïu el resultat en el quadre a continuació ([[Special:Captcha/help|més informació]]):',
109109 'captcha-badlogin' => 'Per a ajudar en la protecció contra l\'obtenció automatitzada de contrasenyes haureu de resoldre la suma que apareix a continuació ([[Special:Captcha/help|més informació]]):',
@@ -121,7 +121,7 @@
122122
123123 Cliqueu el botó de retrocedir del vostre navegador per a tornar al formulari.',
124124 );
125 -$wgConfirmEditMessages['cs'] = array(
 125+$messages['cs'] = array(
126126 'captcha-badlogin' => 'V rámci ochrany před automatickým pokusům uhodnout heslo musíte vyřešit následující jednoduchý součet a napsat výsledek. ([[Special:Captcha/help|Co tohle znamená?]]):',
127127 'captchahelp-cookies-needed' => "Musíte mít zapnuty cookies ve svém prohlížeči.",
128128 'captcha-edit' => 'Abyste mohli editovat tuto stránku, musíte vyřešit následující jednoduchý součet a napsat výsledek. ([[Special:Captcha/help|Co tohle znamená?]])',
@@ -138,7 +138,7 @@
139139 'captcha-createaccount' => 'V rámci ochrany před automatickým vytvářením účtů musíte pro provedení registrace vyřešit následující jednoduchý součet a napsat výsledek. ([[Special:Captcha/help|Co tohle znamená?]])',
140140 'captcha-createaccount-fail' => 'Chybějící či neplatný potvrzovací kód.',
141141 );
142 -$wgConfirmEditMessages['cy'] = array(
 142+$messages['cy'] = array(
143143 'captcha-edit' => "Mae eich golygiad yn cynnwys cysylltiadau URL newydd. Er mwyn profi nad ydych yn beiriant sbam, teipiwch y geiriau canlynol yn y blwch isod os gwelwch yn dda. <br />([[Arbennig:Captcha/help|Mwy o wybodaeth]])",
144144 'captcha-addurl' => "Mae eich golygiad yn cynnwys cysylltiadau URL newydd. Er mwyn profi nad ydych yn beiriant sbam, teipiwch y geiriau canlynol yn y blwch isod os gwelwch yn dda. <br />([[Arbennig:Captcha/help|Mwy o wybodaeth]])",
145145 'captcha-create' => "Mae eich golygiad yn cynnwys cysylltiadau URL newydd. Er mwyn profi nad ydych yn beiriant sbam, teipiwch y geiriau canlynol yn y blwch isod os gwelwch yn dda. <br />([[Arbennig:Captcha/help|Mwy o wybodaeth]])",
@@ -147,7 +147,7 @@
148148 'captcha-createaccount' => "Teipiwch y geiriau sy'n ymddangos yn y ddelwedd isod os gwelwch yn dda. Mae'r nodwedd hon yn rhwystro rhaglenni sbam rhag creu cyfrifon i'w hunain. <br />([[Arbennig:Captcha/help|Mwy o wybodaeth]])",
149149 'captcha-createaccount-fail' => "Côd cadarnhau ar goll neu'n anghywir.",
150150 );
151 -$wgConfirmEditMessages['da'] = array(
 151+$messages['da'] = array(
152152 'captcha-edit' => 'For at redigere denne side, skal du give svaret på regnestyket nedenfor, og angive resultatet i feltet under det. ([[Special:Captcha/help|mere information]]):',
153153 'captcha-addurl' => 'Din redigering tilføjer nye eksterne henvisninger til artiklen. Som beskyttelse mod automatiseret spam, skal du give svaret på regnestyket nedenfor, og angive resultatet i feltet under det. ([[Special:Captcha/help|mere information]]):',
154154 'captcha-badlogin' => 'For at beskytte mod automatiserede gæt på kodeord, skal du give svaret på regnestyket nedenfor, og angive resultatet i feltet under det. ([[Special:Captcha/help|mere information]]):',
@@ -171,7 +171,7 @@
172172 #</pre> <!-- Undlad at rette denne linie -->',
173173 );
174174
175 -$wgConfirmEditMessages['de'] = array(
 175+$messages['de'] = array(
176176 'captcha-edit' => "Zur Bearbeitung der Seite löse die nachfolgende Rechenaufgabe und trage das Ergebnis in das Feld unten ein [[{{ns:special}}:Captcha/help|(Fragen oder Probleme?)]].",
177177 'captcha-addurl' => "Deine Bearbeitung enthält neue externe Links. Zum Schutz vor automatisiertem Spamming löse die nachfolgende Rechenaufgabe und trage das Ergebnis in das Feld unten ein. Klicke dann erneut auf „Seite speichern“ [[{{ns:special}}:Captcha/help|(Fragen oder Probleme?)]].",
178178 'captcha-badlogin' => 'Zum Schutz vor einer Kompromittierung deines Benutzerkontos löse die nachfolgende Rechenaufgabe und trage das Ergebnis in das Feld unten ein [[{{ns:special}}:Captcha/help|(Fragen oder Probleme?)]]:',
@@ -189,7 +189,7 @@
190190 #</pre> <!-- leave this line exactly as it is -->',
191191 );
192192
193 -$wgConfirmEditMessages['es'] = array(
 193+$messages['es'] = array(
194194 'captcha-edit' => 'Para editar este artículo, por favor resuelve la sencilla suma que aparece abajo e introduce la solución en la caja ([[Special:Captcha/help|más información]]):',
195195 'captcha-addurl' => 'Tu edición incluye nuevos enlaces externos. Para ayudar a proteger contra el spam automatizado, por favor resuelve la sencilla suma de abajo e introduce la respuesta en la caja ([[Special:Captcha/help|más información]]):',
196196 'captcha-createaccount' => 'Para ayudar a protegernos de la creación automática de cuentas, por favor resuelve la simple suma de abajo e introduce la respuesta en la caja ([[Special:Captcha/help|más información]]):',
@@ -202,7 +202,7 @@
203203 En ocasiones, especialmente cuando añada nuevos enlaces a una página, la wiki le mostrará una imagen de texto coloreado o distorsionado y le pedirá que escriba las palabras que muestra. Dado que esta es una tarea difícil de automatizar, permite a la mayoría de las personas enviar sus textos, a la vez que detiene a la mayoría de los spammers y otros atacantes automáticos.',
204204 );
205205
206 -$wgConfirmEditMessages['et'] = array(
 206+$messages['et'] = array(
207207 'captcha-edit' => "Teie muudatuses on uusi linke; kaitseks spämmi vastu peate sisestama järgneval pildil olevad sõnad:<br /> ([[Special:Captcha/help|Mis see on?]])",
208208 'captcha-addurl' => "Teie muudatuses on uusi linke; kaitseks spämmi vastu peate sisestama järgneval pildil olevad sõnad:<br /> ([[Special:Captcha/help|Mis see on?]])",
209209 'captcha-create' => "Teie muudatuses on uusi linke; kaitseks spämmi vastu peate sisestama järgneval pildil olevad sõnad:<br /> ([[Special:Captcha/help|Mis see on?]])",
@@ -211,7 +211,7 @@
212212 'captcha-createaccount' => "Kaitsena spämmi vastu peate konto registreerimiseks lahtrisse kirjutama järgneva tehte tulemuse.<br /> ([[Special:Captcha/help|Mis see on?]])",
213213 'captcha-createaccount-fail' => "Puuduv või valesti sisestatud kinnituskood.",
214214 );
215 -$wgConfirmEditMessages['eu'] = array(
 215+$messages['eu'] = array(
216216 'captcha-edit' => "Zure aldaketan URL lotura berriak daude; spam-a saihesteko, jarraian dagoen irudiko hitzak idaztea beharrezkoa da:<br /> ([[Special:Captcha/help|Zer da hau?]])",
217217 'captcha-addurl' => "Zure aldaketan URL lotura berriak daude; spam-a saihesteko, jarraian dagoen irudiko hitzak idaztea beharrezkoa da:<br /> ([[Special:Captcha/help|Zer da hau?]])",
218218 'captcha-create' => "Zure aldaketan URL lotura berriak daude; spam-a saihesteko, jarraian dagoen irudiko hitzak idaztea beharrezkoa da:<br /> ([[Special:Captcha/help|Zer da hau?]])",
@@ -221,7 +221,7 @@
222222 'captcha-createaccount-fail' => "Baieztatze kode ezegokia.",
223223 );
224224
225 -$wgConfirmEditMessages['fi'] = array(
 225+$messages['fi'] = array(
226226 'captcha-edit' => 'Muokkauksesi sisältää uusia linkkejä muille sivuille. Ratkaise alla oleva summa jatkaaksesi ([[Special:Captcha/help|lisätietoja]]):',
227227 'captcha-addurl' => 'Muokkauksesi sisältää uusia linkkejä muille sivuille. Ratkaise alla oleva summa jatkaaksesi ([[Special:Captcha/help|lisätietoja]]):',
228228 'captcha-create' => 'Muokkauksesi sisältää uusia linkkejä muille sivuille. Ratkaise alla oleva summa jatkaaksesi ([[Special:Captcha/help|lisätietoja]]):',
@@ -238,7 +238,7 @@
239239
240240 Voit palata muokkaustilaan selaimen paluutoiminnolla.",
241241 );
242 -$wgConfirmEditMessages['fr'] = array(
 242+$messages['fr'] = array(
243243 'captcha-edit' => 'Votre édition inclut de nouveaux liens externes. Comme protection contre le pourriel automatique, veuillez entrer le résultat de l’opération ci-dessous dans la boîte ([[Special:Captcha/help|plus d’informations]]) :',
244244 'captcha-addurl' => 'Votre édition inclut de nouveaux liens externes. Comme protection contre le pourriel automatique, veuillez entrer le résultat de l’opération ci-dessous dans la boîte ([[Special:Captcha/help|plus d’informations]]) :',
245245 'captcha-badlogin' => 'Pour essayer de contourner les tentatives de crackage de mots de passe automatisées par des robots, veuillez recopier le texte ci-dessous dans la boîte de texte placée au dessous de celui-ci. ([[Special:Captcha/aide|Plus d’infos]])',
@@ -255,7 +255,7 @@
256256
257257 Cliquez sur le bouton « Précédent » de votre navigateur pour revenir à la page d’édition.',
258258 );
259 -$wgConfirmEditMessages['ga'] = array(
 259+$messages['ga'] = array(
260260 'captcha-edit' => "Tá naisc URL nua san athrú seo atá tú ar tí a dhéanamh; mar chosaint in éadan turscair uathoibrithe, caithfidh tú na focail san íomhá seo a ionchur: <br />([[Speisialta:Captcha/help|Céard é seo?]])",
261261 'captcha-addurl' => "Tá naisc URL nua san athrú seo atá tú ar tí a dhéanamh; mar chosaint in éadan turscair uathoibrithe, caithfidh tú na focail san íomhá seo a ionchur: <br />([[Speisialta:Captcha/help|Céard é seo?]])",
262262 'captcha-create' => "Tá naisc URL nua san athrú seo atá tú ar tí a dhéanamh; mar chosaint in éadan turscair uathoibrithe, caithfidh tú na focail san íomhá seo a ionchur: <br />([[Speisialta:Captcha/help|Céard é seo?]])",
@@ -270,7 +270,7 @@
271271 'captcha-createaccount' => "Mar chosaint in éadan turscair uathoibrithe, caithfidh tú na focail san íomhá seo a ionchur chun cuntas a chlárú: <br />([[Speisialta:Captcha/help|Céard é seo?]])",
272272 'captcha-createaccount-fail' => "Ní raibh an cód deimhnithe ceart sa bhosca, nó ní raibh aon chód ann ar chor ar bith.",
273273 );
274 -$wgConfirmEditMessages['gl'] = array(
 274+$messages['gl'] = array(
275275 'captcha-edit' => 'A súa edición inclúe novos enderezos URL; como protección contra as ferramentas de publicación automática de ligazóns publicitarias necesita teclear as palabras que aparecen nesta imaxe:<br /> ([[Special:Captcha/help|Qué é isto?]])',
276276 'captcha-addurl' => 'A súa edición inclúe novos enderezos URL; como protección contra as ferramentas de publicación automática de ligazóns publicitarias necesita teclear as palabras que aparecen nesta imaxe:<br /> ([[Special:Captcha/help|Qué é isto?]])',
277277 'captcha-badlogin' => 'Como protección para que non descubran o contrasinal por medios automáticos, resolva a suma simple de embaixo e introduza a resposta na caixa ([[Special:Captcha/help|máis información]])',
@@ -286,7 +286,7 @@
287287 # * Cada liña que non estea en branco é un fragmento de expresión regular que só coincidirá con hosts dentro de URLs
288288 #</pre> <!-- deixe esta liña exactamente como está -->',
289289 );
290 -$wgConfirmEditMessages['he'] = array(
 290+$messages['he'] = array(
291291 'captcha-edit' => 'כדי לערוך את הדף, אנא פיתרו את תרגיל החיבור הפשוט שלהלן והקלידו את התשובה בתיבה ([[{{ns:special}}:Captcha/help|מידע נוסף]]):',
292292 'captcha-addurl' => 'עריכתכם כוללת קישורים חיצוניים חדשים. כהגנה מפני ספאם אוטומטי, אנא פיתרו את תרגיל החיבור הפשוט שלהלן והקלידו את התשובה בתיבה ([[{{ns:special}}:Captcha/help|מידע נוסף]]):',
293293 'captcha-badlogin' => 'כהגנה מפני פריצת סיסמאות אוטומטית, אנא פיתרו את תרגיל החיבור הפשוט שלהלן והקלידו את התשובה בתיבה ([[{{ns:special}}:Captcha/help|מידע נוסף]]):',
@@ -309,7 +309,7 @@
310310 # * כל שורה לא ריקה היא ביטוי רגולרי שיתאים לאתרים בכתובות URL
311311 #</pre> <!-- יש להשאיר שורה זו בדיוק כפי שהיא כתובה -->',
312312 );
313 -$wgConfirmEditMessages['hr'] = array(
 313+$messages['hr'] = array(
314314 'captcha-edit' => "Vaše uređivanje sadrži nove vanjske poveznice. Kao zaštitu od automatskog spama, trebate unijeti slova koja vidite na slici: <br />([[Posebno:Captcha/help|Pomoć?]])",
315315 'captcha-addurl' => "Vaše uređivanje sadrži nove vanjske poveznice. Kao zaštitu od automatskog spama, trebate unijeti slova koja vidite na slici: <br />([[Posebno:Captcha/help|Pomoć?]])",
316316 'captcha-create' => "Vaše uređivanje sadrži nove vanjske poveznice. Kao zaštitu od automatskog spama, trebate unijeti slova koja vidite na slici: <br />([[Posebno:Captcha/help|Pomoć?]])",
@@ -318,7 +318,7 @@
319319 'captcha-createaccount' => "Kao zaštitu od automatskog spama, pri otvaranju računa trebate unijeti slova koja vidite na slici: <br />([[Posebno:Captcha/help|Pomoć]])",
320320 'captcha-createaccount-fail' => "Potvrdni kod je nepotpun ili netočan.",
321321 );
322 -$wgConfirmEditMessages['hsb'] = array(
 322+$messages['hsb'] = array(
323323 'captcha-edit' => 'W twojej změnje su nowe eksterne wotkazy. Jako škitna naprawa přećiwo spamej dyrbiš slědowacy nadawk wuličeć a wuslědk do kašćika zapisować. Klikń potom znowa na „Składować”.<br /> [[{{ns:special}}:Captcha/help|(Čehodla?)]]',
324324 'captcha-addurl' => 'W twojej změnje su nowe eksterne wotkazy. Jako škitna naprawa přećiwo spamej dyrbiš slědowacy nadawk wuličeć a wuslědk do kašćika zapisować. Klikń potom znowa na „Składować”.<br /> [[{{ns:special}}:Captcha/help|(Čehodla?)]]',
325325 'captcha-badlogin' => 'Zo by so awtomatiskemu zadobywanju do hesłow zadźěwało, dyrbiš slědowacy nadawk wuličeć a wuslědk do kašćika zapisować. [[{{ns:special}}:Captcha/help|(Prašenja abo problemy?)]]',
@@ -335,7 +335,7 @@
336336
337337 #</pre> <!-- leave this line exactly as it is -->',
338338 );
339 -$wgConfirmEditMessages['id'] = array(
 339+$messages['id'] = array(
340340 'captcha-edit' => "Suntingan Anda menyertakan pralana luar baru. Sebagai perlindungan terhadap ''spam'' otomatis, Anda harus mengetikkan kata atau hasil perhitungan yang tertera berikut ini:<br />
341341 ([[Special:Captcha/help|info lengkap]])",
342342 'captcha-addurl' => "Suntingan Anda menyertakan pralana luar baru. Sebagai perlindungan terhadap ''spam'' otomatis, Anda harus mengetikkan kata atau hasil perhitungan yang tertera berikut ini:<br />
@@ -356,7 +356,7 @@
357357 ([[Special:Captcha/help|info lengkap]])",
358358 'captcha-createaccount-fail' => "Kode konfirmasi salah atau belum diisi.",
359359 );
360 -$wgConfirmEditMessages['is'] = array(
 360+$messages['is'] = array(
361361 'captcha-edit' => "Breyting þín fól í sér nýja tengla á aðrar vefsíður. Til þess að verjast sjálfvirku auglýsingarusli verðum við að biðja þig um að skrifa inn orðin sem sjást á þessari mynd: <br />([[Special:Captcha/help|Hvað er þetta?]])",
362362 'captcha-addurl' => "Breyting þín fól í sér nýja tengla á aðrar vefsíður. Til þess að verjast sjálfvirku auglýsingarusli verðum við að biðja þig um að skrifa inn orðin sem sjást á þessari mynd: <br />([[Special:Captcha/help|Hvað er þetta?]])",
363363 'captcha-create' => "Breyting þín fól í sér nýja tengla á aðrar vefsíður. Til þess að verjast sjálfvirku auglýsingarusli verðum við að biðja þig um að skrifa inn orðin sem sjást á þessari mynd: <br />([[Special:Captcha/help|Hvað er þetta?]])",
@@ -371,7 +371,7 @@
372372 'captcha-createaccount' => "Til þess að verjast sjálfvirku auglýsingarusli verðum við að biðja þig um að skrifa inn orðin sem sjást á þessari mynd áður en þú skráir notandanafn: <br />([[Special:Captcha/help|Hvað er þetta?]])",
373373 'captcha-createaccount-fail' => "Staðfestingarkóðinn var rangur eða ekki til staðar.",
374374 );
375 -$wgConfirmEditMessages['it'] = array(
 375+$messages['it'] = array(
376376 'captcha-edit' => 'La modifica richiesta aggiunge dei nuovi collegamenti (URL) alla pagina; come misura precauzionale contro l\'inserimento automatico di spam, per confermarla è necessario inserire le parole che appaiono nell\'immagine:<br />
377377 ([[Special:Captcha/help|Cosa vuol dire?]])',
378378 'captcha-addurl' => 'La modifica richiesta aggiunge dei nuovi collegamenti (URL) alla pagina; come misura precauzionale contro l\'inserimento automatico di spam, per confermarla è necessario inserire le parole che appaiono nell\'immagine:<br />
@@ -393,7 +393,7 @@
394394 Fare clic sul pulsante \'back\' del browser per tornare alla pagina di modifica.',
395395 );
396396
397 -$wgConfirmEditMessages['ja'] = array(
 397+$messages['ja'] = array(
398398 'captcha-edit' => 'このページを編集するには下記に現れる数式の答えを入力してください。<br />
399399 ([[Special:Captcha/help|詳細]])',
400400 'captcha-addurl' => 'あなたの編集には新たに外部リンクが追加されています。スパム防止のため、下記の数式の答えを入力してください<br />
@@ -416,7 +416,7 @@
417417 編集ページに戻るには、ブラウザの戻るボタンを押してください。',
418418 );
419419
420 -$wgConfirmEditMessages['kk-kz'] = array(
 420+$messages['kk-kz'] = array(
421421 'captcha-edit' => 'Бұл бетті өңдеу үшін, төмендегі қосындылауды шешіңіз де, нәтижесін
422422 аумаққа енгізіңіз ([[{{ns:special}}:Captcha/help|көбірек ақпарат]]):',
423423 'captcha-addurl' => 'Түзетуіңізде жаңа сыртқы сілтемелер бар екен. Өздіктік «спам» жасалуынан қорғану үшін,
@@ -440,7 +440,7 @@
441441
442442 Бет өңдеуіне қайту бару үшін «Артқа» деген түймесін басыңыз."
443443 );
444 -$wgConfirmEditMessages['kk-tr'] = array(
 444+$messages['kk-tr'] = array(
445445 'captcha-edit' => 'Bul betti öñdew üşin, tömendegi qosındılawdı şeşiñiz de, nätïjesin
446446 awmaqqa engiziñiz ([[{{ns:special}}:Captcha/help|köbirek aqparat]]):',
447447 'captcha-addurl' => 'Tüzetwiñizde jaña sırtqı siltemeler bar eken. Özdiktik «spam» jasalwınan qorğanw üşin,
@@ -464,7 +464,7 @@
465465
466466 Bet öñdewine qaýtw barw üşin «Artqa» degen tüýmesin basıñız."
467467 );
468 -$wgConfirmEditMessages['kk-cn'] = array(
 468+$messages['kk-cn'] = array(
469469 'captcha-edit' => 'بۇل بەتتٸ ٶڭدەۋ ٷشٸن, تٶمەندەگٸ قوسىندىلاۋدى شەشٸڭٸز دە, نٵتيجەسٸن
470470 اۋماققا ەنگٸزٸڭٸز ([[{{ns:special}}:Captcha/help|كٶبٸرەك اقپارات]]):',
471471 'captcha-addurl' => 'تٷزەتۋٸڭٸزدە جاڭا سىرتقى سٸلتەمەلەر بار ەكەن. ٶزدٸكتٸك «سپام» جاسالۋىنان قورعانۋ ٷشٸن,
@@ -488,11 +488,11 @@
489489
490490 بەت ٶڭدەۋٸنە قايتۋ بارۋ ٷشٸن «ارتقا» دەگەن تٷيمەسٸن باسىڭىز."
491491 );
492 -$wgConfirmEditMessages['kk'] = $wgConfirmEditMessages['kk-kz'];
493 -$wgConfirmEditMessages['ko'] = array(
 492+$messages['kk'] = $messages['kk-kz'];
 493+$messages['ko'] = array(
494494 'captcha-createaccount' => '자동 가입을 막기 위해, 아래 문제의 답을 적어야만 가입이 가능합니다([[Special:Captcha/help|관련 도움말]]):',
495495 );
496 -$wgConfirmEditMessages['la'] = array(
 496+$messages['la'] = array(
497497 'captcha-edit' => 'Ad hanc paginam recensendum, necesse est tibi solvere calculationem subter et responsum in capsam inscribere ([[Special:Captcha/help|Quidst illud?]]):',
498498 'captcha-addurl' => 'Emendatione tua insunt nexus externi; ut spam automaticum vitemus, necesse est tibi solvere calculationem subter et responsum in capsam inscribere ([[Special:Captcha/help|Quidst illud?]]):',
499499 'captcha-badlogin' => 'Ut vitemus ne tesserae frangantur, necesse est tibi solvere calculationem subter et responsum in capsam inscribere ([[Special:Captcha/help|Quidst illud?]]):',
@@ -501,14 +501,14 @@
502502 'captcha-create' => 'Ad paginam creandum, necesse est tibi solvere calculationem subter et responsum in capsam inscribere ([[Special:Captcha/help|Quidst illud?]]):',
503503 'captchahelp-title' => 'Captcha auxilium',
504504 );
505 -$wgConfirmEditMessages['lo'] = array(
 505+$messages['lo'] = array(
506506 'captcha-edit' => 'ການດັດແກ້ ຂອງ ທ່ານ ມີລິ້ງູຄ໌ພາຍນອກ. ເພື່ອ ເປັນການຊ່ອຍປ້ອງກັນ ສະແປມອັດຕະໂນມັດ, ກະລຸນາແກ້ເລກບວກ ງ່າຍໆຂ້າງລຸ່ມນີ້ ແລ້ວ ພິມຄຳຕອບໃສ່ໃນ ກັບ ([[Special:Captcha/help|more info]]):',
507507 'captcha-addurl' => 'ການດັດແກ້ຂອງທ່ານ ມີ ການກາງລິ້ງຄ໌ຫາພາຍນອກ. ເພື່ອເປັນການຊ່ອຍປ້ອງກັນ ສະແປມອັດຕະໂນມັດ ກະລຸນາ ແກ້ເລກບວກງ່າຍໆຂ້າງລຸ່ມນີ້ ແລ້ວ ພິມຜົນບວກ ໃສ່ ກັບ ([[Special:Captcha/help|ຂໍ້ມູນເພີ່ມເຕີມ]]):',
508508 'captcha-createaccount' => 'ເພື່ອປ້ອງກັນ ການສ້າງບັນຊີແບບອັດຕະໂນມັດ, ກະລຸນາ ແກ້ເລກບວກງ່າຍໆ ຂ້າງລຸ່ມ ແລ້ວ ພິມຄຳຕອບໃສ່ ກັບ ([[Special:Captcha/help|more info]]):',
509509 'captcha-createaccount-fail' => "ບໍ່ຖືກ ຫຼື ບໍ່ມີລະຫັດຢືນຢັນ.",
510510 'captcha-create' => 'ກະລຸນາ ແກ້ເລກບວກງ່າຍໆລຸ່ມນີ້ ແລະ ພິມຜົນບວກໃສ່ໃນກັບ ເພື່ອ ສ້າງໜ້ານີ້ ([[Special:Captcha/help|ຂໍ້ມູນເພີ່ມເຕີມ]]):',
511511 );
512 -$wgConfirmEditMessages['lv'] = array(
 512+$messages['lv'] = array(
513513 'captcha-edit' => "Tavas izmaiņas ietver jaunu URL saiti. Lai pasargātos no automātiskas mēstuļošanas, Tev ir jāieraksta vārds, kas redzams šajā attēlā: <br />([[Special:Captcha/help|Kāpēc tā?]])",
514514 'captcha-addurl' => "Tavas izmaiņas ietver jaunu URL saiti. Lai pasargātos no automātiskas mēstuļošanas, Tev ir jāieraksta vārds, kas redzams šajā attēlā: <br />([[Special:Captcha/help|Kāpēc tā?]])",
515515 'captcha-create' => "Tavas izmaiņas ietver jaunu URL saiti. Lai pasargātos no automātiskas mēstuļošanas, Tev ir jāieraksta vārds, kas redzams šajā attēlā: <br />([[Special:Captcha/help|Kāpēc tā?]])",
@@ -517,12 +517,12 @@
518518 'captcha-createaccount' => "Lai pasargātos no automātiskas mēstuļošanas, Tev reģistrējoties ir jāieraksta vārds, kas redzams šajā attēlā: <br />([[Special:Captcha/help|Kāpēc tā?]])",
519519 'captcha-createaccount-fail' => "Nepareizs apstiprinājuma kods vai arī tas nav ievadīts.",
520520 );
521 -$wgConfirmEditMessages['nan'] = array(
 521+$messages['nan'] = array(
522522 'captcha-createaccount' => "Ūi beh ī-hông lâng iōng ke-si chū-tōng chù-chheh koh tah kóng-kò, chhiáⁿ lí kā chhut-hiān tī ang-á lāi-bīn ê jī phah 1 piàn (thang chèng-bêng lí m̄ sī ki-khì-lâng): <br />
523523 ([[Special:Captcha/help|Che sī siáⁿ-hòe?]])",
524524 'captcha-createaccount-fail' => "Khak-jīn-bé chhò-gō· iah-sī làu-kau.",
525525 );
526 -$wgConfirmEditMessages['nds'] = array(
 526+$messages['nds'] = array(
527527 'captcha-edit' => 'In dien Text steiht en nee Lenk na buten dat Wiki. Dat hier keen automaatsch instellten Spam rinkummt, musst du disse lütte Rekenopgaav lösen ([[Special:Captcha/help|mehr dorto]]):',
528528 'captcha-createaccount' => 'Dat hier nich Brukers automaatsch anleggt warrt, musst du disse lütte Rekenopgaav lösen ([[Special:Captcha/help|mehr dorto]]):',
529529 'captcha-createaccount-fail' => 'Kood to’n Bestätigen is verkehrt oder fehlt.',
@@ -539,7 +539,7 @@
540540
541541 #</pre> <!-- leave this line exactly as it is -->',
542542 );
543 -$wgConfirmEditMessages['nl'] = array(
 543+$messages['nl'] = array(
544544 'captcha-edit' => 'Uw bewerking bevat nieuwe externe links (URL\'s). Voer ter bescherming tegen geautomatiseerde spam de woorden in die in de volgende afbeelding te zien zijn:<br />
545545 ([[Special:Captcha/help|Wat is dit?]])',
546546 'captcha-addurl' => 'Uw bewerking bevat nieuwe externe links (URL\'s). Voer ter bescherming tegen geautomatiseerde spam de woorden in die in de volgende afbeelding te zien zijn:<br />
@@ -565,7 +565,7 @@
566566 # * Iedere niet-lege regel is een fragment van een reguliere expressie die alleen van toepassing is op hosts binnen URL\'s
567567 #</pre> <!-- leave this line exactly as it is -->',
568568 );
569 -$wgConfirmEditMessages['no'] = array(
 569+$messages['no'] = array(
570570 'captcha-edit' => 'For å redigere denne artikkelen, vennligst skriv inn summen nedenfor i boksen ([[Special:Captcha/help|mer informasjon]]):',
571571 'captcha-addurl' => 'Din redigering inneholder nye eksterne lenker. For å hjelpe oss å beskytte oss mot automatisk spam, vennligst skriv inn summen av dette enkle regnestykket i boksen nedenfor ([[Special:Captcha/help|mer informasjon]]):',
572572 'captcha-badlogin' => 'For å hjelpe oss med å beskytte oss mot automatisk passordtyveri, vennligst løs det enkle regnestykket nedenfor og skriv inn svaret i bosken ([[Special:Captcha/help|mer informasjon]]):',
@@ -587,7 +587,7 @@
588588 # * Alle linjer som ikke er blanke er fragmenter av regulære uttrykk som sjekker verter i URL-er
589589 #</pre> <!-- leave this line exactly as it is -->',
590590 );
591 -$wgConfirmEditMessages['nn'] = array(
 591+$messages['nn'] = array(
592592 'captcha-edit' => "Endringa di inkluderer nye lenkjer; som eit vern mot automatisert reklame (spam) er du nøydd til skrive inn orda i dette bildet: <br />([[Special:Captcha/help|Kva er dette?]])",
593593 'captcha-addurl' => "Endringa di inkluderer nye lenkjer; som eit vern mot automatisert reklame (spam) er du nøydd til skrive inn orda i dette bildet: <br />([[Special:Captcha/help|Kva er dette?]])",
594594 'captcha-create' => "Endringa di inkluderer nye lenkjer; som eit vern mot automatisert reklame (spam) er du nøydd til skrive inn orda i dette bildet: <br />([[Special:Captcha/help|Kva er dette?]])",
@@ -596,7 +596,7 @@
597597 'captcha-createaccount' => "For å verne Wikipedia mot reklame (spam) må du skrive inn orda i biletet for å registrere ein konto. <br />([[Special:Captcha/help|Kva er dette?]])",
598598 'captcha-createaccount-fail' => "Feil eller manglande godkjenningskode.",
599599 );
600 -$wgConfirmEditMessages['oc'] = array(
 600+$messages['oc'] = array(
601601 'captcha-edit' => 'Vòstra modificacion inclutz de ligams URL novèla ; per empachar las connexions automatizadas, devètz picar los mots que s’afichan dins l’imatge que seguís : <br />([[Special:Captcha/help|Qu\'es aquò?]])',
602602 'captcha-addurl' => 'Vòstra modificacion inclutz de ligams URL novèla ; per empachar las connexions automatizadas, devètz picar los mots que s’afichan dins l’imatge que seguís : <br />([[Special:Captcha/help|Qu\'es aquò?]])',
603603 'captcha-badlogin' => 'Per ensajar de contornar las temptativas de cracatge de senhals automatizadas per de robòts, recopiatz lo tèxt çai jos dins la boîta de tèxt plaçada al dejos d\'aqueste. ([[Special:Captcha/help|Mai d’entre-senhas]])',
@@ -608,7 +608,7 @@
609609 'captchahelp-cookies-needed' => 'Devètz aver los cookies activats dins vòstre navegaire per qu\'aquò foncione.',
610610 'captchahelp-text' => 'Los sites webs que permeton al mai grand nombre de participar, coma aqueste wiki, son sovent atacats per de spammers qu\'utilizan d\'espleches automatizas per mandar lor ligams sus de fòrça sites sulcòp. Son fòrt aisits de suprimir mas avèm francament de causas mai risolièras de far. De còps quand ajustatz de ligams novèls vèrs lo web, lo wiki pòt vos mostrar un imatge amb un tèxt coloriat o torçut e vos demandar de lo picar. Es una tasca relativament complicada d\'automatizar, çò que permet de diferenciar un uman real d\'un logicial automatic malvolent. Malaürosament, aqueste sistèma es pas adaptat a d\'utilizaires mal-vesents o utilizant de navigaires textuals o audiò. Actualament, prepausem pas d\'alternativas adaptadas. Se avètz besonh d\'ajuda esitetz pas a contactar los administrators del sit. Clicatz sul boton \'precedent\' de vòstre navegaire per tornar a l\'editor.',
611611 );
612 -$wgConfirmEditMessages['pl'] = array(
 612+$messages['pl'] = array(
613613 'captcha-edit' => 'Aby edytować tę stronę musisz rozwiązać proste działanie matematyczne poniżej i wpisać wynik do pola tekstowego ([[Special:Captcha/help|wyjaśnienie]]):',
614614 'captcha-addurl' => 'Twoja edycja zawiera nowe linki zewnętrzne. Ze względu na ochronę przed zautomatyzowanym spamem prosimy wykonać proste działanie matematyczne i wpisać wynik w pole tekstowe ([[Special:Captcha/help|więcej informacji]]):',
615615 'captcha-badlogin' => 'Ze względu na zabezpieczenie przed automatycznym łamaniem haseł prosimy o rozwiązanie tego prostego zadania i wpisanie odwiedzi w pole obok ([[Special:Captcha/help|więcej informacji]])',
@@ -624,7 +624,7 @@
625625 # * Każda linia, która nie jest pusta, jest fragmentem wyrażenia regularnego, które ma pasować do adresów wewnątrz adresów URL
626626 #</pre> <!-- zostaw tę linię dokładnie jak jest -->',
627627 );
628 -$wgConfirmEditMessages['pms'] = array(
 628+$messages['pms'] = array(
629629 'captcha-edit' => 'Për fe-ie dle modìfiche ansima a st\'artìcol-sì, për piasì ch\'a fasa ël total ambelessì sota
630630 e ch\'a buta l\'arzulta ant ël quadrèt ([[Special:Captcha/help|për savejne dë pì]]):',
631631 'captcha-addurl' => 'Soa modìfica a la gionta dj\'anliure esterne. Për giutene a vardesse da la reclam aotomatisà, për piasì ch\'a fasa ël total ambelessì sota e ch\'a buta l\'arzultà ant ël quadrèt ([[Special:Captcha/help|për savejne dë pì]]):',
@@ -649,7 +649,7 @@
650650 # * minca riga nen veujda a l\'é un frament d\'espression regolar ch\'as dòvra për identifiché j\'adrësse dle màchine servente ant j\'anliure
651651 #</pre> <!-- leave this line exactly as it is -->',
652652 );
653 -$wgConfirmEditMessages['pt'] = array(
 653+$messages['pt'] = array(
654654 'captcha-edit' => 'Para editar esta página será necessário que você digite as palavras exibidas na seguinte imagem no box apropriado ([[Special:Captcha/help|o que é isto?]])',
655655 'captcha-addurl' => 'Sua edição inclui novas ligações externas; como prevenção contra sistemas automatizados que inserem spam, será necessário que você digite as palavras exibidas na seguinte imagem no box apropriado ([[Special:Captcha/help|o que é isto?]])',
656656 'captcha-badlogin' => 'Como prevenção contra formas automatizadas de pesquisa e descoberta de senhas, será necessário que você digite as palavras exibidas na seguinte imagem no box apropriado ([[Special:Captcha/help|o que é isto?]])',
@@ -672,8 +672,8 @@
673673 # * Todas as linhas que não estiverem em branco são um fragmento de regex, as quais referem-se aos apenas através de URLs;
674674 #</pre> <!-- mantenha esta linha exatamente desta forma -->',
675675 );
676 -$wgConfirmEditMessages['pt-br'] = $wgConfirmEditMessages['pt'];
677 -$wgConfirmEditMessages['ro'] = array(
 676+$messages['pt-br'] = $messages['pt'];
 677+$messages['ro'] = array(
678678 'captcha-edit' => 'Editarea include legături externe noi. Pentru a evita spam-ul automat, vă rugăm să rezolvaţi adunarea de mai jos şi introduceţi rezultatul în căsuţă ([[Special:Captcha/help|detalii]]):',
679679 'captcha-addurl' => 'Editarea include legături externe noi. Pentru a evita spam-ul automat, vă rugăm să rezolvaţi adunarea de mai jos şi introduceţi rezultatul în căsuţă ([[Special:Captcha/help|detalii]]):',
680680 'captcha-badlogin' => 'Ca măsură de protecţie împotriva spargerii de parole, vă rugăm să rezolvaţi adunarea de mai jos şi introduceţi rezultatul în căsuţă ([[Special:Captcha/help|detalii]]):',
@@ -690,7 +690,7 @@
691691
692692 Va fi nevoie ca browserul folosit să suporte module cookie.',
693693 );
694 -$wgConfirmEditMessages['ru'] = array(
 694+$messages['ru'] = array(
695695 'captcha-edit' => "Вы добавили ссылку на внешний сайт; в целях защиты от автоматического спама, введите буквы изображённые на картинке:<br />
696696 ([[{{ns:special}}:Captcha/help|Что это такое?]])",
697697 'captcha-addurl' => "Вы добавили ссылку на внешний сайт; в целях защиты от автоматического спама, введите буквы изображённые на картинке:<br />
@@ -709,7 +709,7 @@
710710 ([[{{ns:special}}:Captcha/help|Что это такое?]])",
711711 'captcha-createaccount-fail' => "Код подтверждения отсутствует или неверен.",
712712 );
713 -$wgConfirmEditMessages['sk'] = array(
 713+$messages['sk'] = array(
714714 'captcha-edit' => 'Vaša úprava obsahuje nové externé odkazy. Ako pomoc pri ochrane pred automatickým spamom vyriešte prosím tento jednoduchý súčet a zadajte výsledok do poľa ([[Special:Captcha/help|viac informácií]]):',
715715 'captcha-addurl' => 'Vaša úprava obsahuje nové externé odkazy. Ako pomoc pri ochrane pred automatickým spamom vyriešte prosím tento jednoduchý súčet a zadajte výsledok do poľa ([[Special:Captcha/help|viac informácií]]):',
716716 'captcha-badlogin' => 'Ako ochranu proti automatizovanému lámaniu hesiel, prosím vyriešte nasledujúci súčet a zadajte ho do poľa pre odpoveď ([[Special:Captcha/help|viac informácií]]):',
@@ -733,7 +733,7 @@
734734 #</pre> <!-- leave this line exactly as it is -->',
735735 );
736736
737 -$wgConfirmEditMessages['sl'] = array(
 737+$messages['sl'] = array(
738738 'captcha-edit' => "Vaše urejanje vključuje nove URL-povezave; zaradi zaščite pred avtomatizirano navlako boste morali vpisati besede, ki se pojavijo v okencu: <br />([[{{ns:Special}}:Captcha/help|Kaj je to?]])",
739739 'captcha-addurl' => "Vaše urejanje vključuje nove URL-povezave; zaradi zaščite pred avtomatizirano navlako boste morali vpisati besede, ki se pojavijo v okencu: <br />([[{{ns:Special}}:Captcha/help|Kaj je to?]])",
740740 'captcha-create' => "Vaše urejanje vključuje nove URL-povezave; zaradi zaščite pred avtomatizirano navlako boste morali vpisati besede, ki se pojavijo v okencu: <br />([[{{ns:Special}}:Captcha/help|Kaj je to?]])",
@@ -748,7 +748,7 @@
749749 'captcha-createaccount' => "Za registracijo je zaradi zaščite pred neželenimi reklamnimi sporočili treba vpisati prikazane besede: <br />([[{{ns:special}}:Captcha|Kaj je to?]])",
750750 'captcha-createaccount-fail' => "Nepravilna ali manjkajoča potrditvena koda.",
751751 );
752 -$wgConfirmEditMessages['sq'] = array(
 752+$messages['sq'] = array(
753753 'captcha-edit' => 'Redaktimi juaj ka lidhje URL të reja dhe si mbrojtje kundër abuzimeve automatike duhet të shtypni çfarë shfaqet tek figura e mëposhtme:<br /> ([[Special:Captcha|Çfarë është kjo?]])',
754754 'captcha-addurl' => 'Redaktimi juaj ka lidhje URL të reja dhe si mbrojtje kundër abuzimeve automatike duhet të shtypni çfarë shfaqet tek figura e mëposhtme:<br /> ([[Special:Captcha|Çfarë është kjo?]])',
755755 'captcha-create' => 'Redaktimi juaj ka lidhje URL të reja dhe si mbrojtje kundër abuzimeve automatike duhet të shtypni çfarë shfaqet tek figura e mëposhtme:<br /> ([[Special:Captcha|Çfarë është kjo?]])',
@@ -764,7 +764,7 @@
765765 'captcha-createaccount-fail' => 'Mesazhi që duhej shtypur mungon ose nuk është shtypur siç duhet.',
766766 );
767767
768 -$wgConfirmEditMessages['su'] = array(
 768+$messages['su'] = array(
769769 'captcha-edit' => 'Pikeun ngédit artikel ieu, mangga eusian itungan di handap ieu ([[Special:Captcha/help|émbaran lengkep]]):',
770770 'captcha-addurl' => 'Éditan anjeun ngawengku tumbu kaluar anyar. Pikeun nyegah spam, mangga eusian itungan di handap ieu [[Special:Captcha/help|émbaran lengkep]]):',
771771 'captcha-createaccount' => 'Pikeun nyegah dijieunna rekening sacara otomatis, mangga eusian itungan di handap ieu ([[Special:Captcha/help|émbaran lengkep]]):',
@@ -782,7 +782,7 @@
783783 Hit the \'back\' button in your browser to return to the page editor.',
784784 );
785785
786 -$wgConfirmEditMessages['sv'] = array(
 786+$messages['sv'] = array(
787787 'captcha-edit' => 'För att redigera den här sidan måste du först skriva svaret på följande
788788 räkneuppgift i rutan ([[Special:Captcha/help|mer information]]):',
789789 'captcha-addurl' => 'Din ändring lägger till nya externa länkar i texten. För att skydda wikin mot
@@ -817,7 +817,7 @@
818818 #</pre> <!-- leave this line exactly as it is -->',
819819 );
820820
821 -$wgConfirmEditMessages['uk'] = array(
 821+$messages['uk'] = array(
822822 'captchahelp-text' => "Вікіпедія застосовує техніку розрізнення людей від комп'ютерів, яка використовує розпізнавання образів, для захисту від комп'ютерних шкідливих програм, які автоматично реєструються (найчастіше спамлять у статтях).
823823
824824 Для реєстрації у Вікіпедії та іноді й при редагуванні статей користувачеві потрібно ввести вказану контрольну послідовність символів, і яку вони, будучи людьми, а не комп'ютерними програмами, можуть легко розпізнати.
@@ -827,7 +827,7 @@
828828 Hit the 'back' button in your browser to return to the page editor.",
829829 'captcha-createaccount-fail' => 'Невірний або відсутній код підтвердження.',
830830 );
831 -$wgConfirmEditMessages['wa'] = array(
 831+$messages['wa'] = array(
832832 'captcha-edit' => 'Dins vos candjmints i gn a des novelès hårdêyes (URL); po s\' mete a houte des robots di spam, nos vs dimandans d\' acertiner ki vos estoz bén ene djin, po çoula, tapez les mots k\' aparexhèt dins l\' imådje chal pa dzo:<br />([[{{ns:special}}:Captcha/help|Pocwè fjhans ns çoula?]])',
833833 'captcha-addurl' => 'Dins vos candjmints i gn a des novelès hårdêyes (URL); po s\' mete a houte des robots di spam, nos vs dimandans d\' acertiner ki vos estoz bén ene djin, po çoula, tapez les mots k\' aparexhèt dins l\' imådje chal pa dzo:<br />([[{{ns:special}}:Captcha/help|Pocwè fjhans ns çoula?]])',
834834 'captcha-create' => 'Dins vos candjmints i gn a des novelès hårdêyes (URL); po s\' mete a houte des robots di spam, nos vs dimandans d\' acertiner ki vos estoz bén ene djin, po çoula, tapez les mots k\' aparexhèt dins l\' imådje chal pa dzo:<br />([[{{ns:special}}:Captcha/help|Pocwè fjhans ns çoula?]])',
@@ -843,7 +843,7 @@
844844 'captcha-createaccount' => 'Po s\' mete a houte des robots di spam, nos vs dimandans d\' acertiner ki vos estoz bén ene djin po-z ahiver vosse conte, po çoula, tapez les mots k\' aparexhèt dins l\' imådje chal pa dzo:<br />([[{{ns:special}}:Captcha/help|Pocwè fjhans ns çoula?]])',
845845 'captcha-createaccount-fail' => 'Li côde d\' acertinaedje est incorek ou mancant.',
846846 );
847 -$wgConfirmEditMessages['yue'] = array(
 847+$messages['yue'] = array(
848848 'captcha-edit' => "你編輯的內容中含有新的URL連結;為咗避免受到自動垃圾程式的侵擾,你需要輸入顯示喺下面圖片度嘅文字:<br />
849849 ([[Special:Captcha/help|呢個係乜嘢嚟?]])",
850850 'captcha-addurl' => "你編輯的內容中含有新的URL連結;為咗避免受到自動垃圾程式的侵擾,你需要輸入顯示喺下面圖片度嘅文字:<br />
@@ -870,7 +870,7 @@
871871 # * 所有非空白行係一個regex部份,只係會同裏面嘅URL主機相符
872872 #</pre> <!-- leave this line exactly as it is -->',
873873 );
874 -$wgConfirmEditMessages['zh-hans'] = array(
 874+$messages['zh-hans'] = array(
875875 'captcha-edit' => "你编辑的内容中含有一个新的URL链接;为了免受自动垃圾程序的侵扰,你需要输入显示在下面图片中的文字:<br />
876876 ([[Special:Captcha/help|这是什么?]])",
877877 'captcha-addurl' => "你编辑的内容中含有一个新的URL链接;为了免受自动垃圾程序的侵扰,你需要输入显示在下面图片中的文字:<br />
@@ -897,7 +897,7 @@
898898 # * 所有非空白行是一个regex部份,只是跟在里面的URL主机相符
899899 #</pre> <!-- leave this line exactly as it is -->',
900900 );
901 -$wgConfirmEditMessages['zh-hant'] = array(
 901+$messages['zh-hant'] = array(
902902 'captcha-edit' => "你編輯的內容中含有一個新的URL連結;為了免受自動垃圾程式的侵擾,你需要輸入顯示在下面圖片中的文字:<br />
903903 ([[Special:Captcha/help|這是什麼?]])",
904904 'captcha-addurl' => "你編輯的內容中含有一個新的URL連結;為了免受自動垃圾程式的侵擾,你需要輸入顯示在下面圖片中的文字:<br />
@@ -924,10 +924,10 @@
925925 # * 所有非空白行是一個regex部份,只是跟在裏面的URL主機相符
926926 #</pre> <!-- leave this line exactly as it is -->',
927927 );
928 -$wgConfirmEditMessages['zh'] = $wgConfirmEditMessages['zh-hans'];
929 -$wgConfirmEditMessages['zh-cn'] = $wgConfirmEditMessages['zh-hans'];
930 -$wgConfirmEditMessages['zh-hk'] = $wgConfirmEditMessages['zh-hant'];
931 -$wgConfirmEditMessages['zh-min-nan'] = $wgConfirmEditMessages['nan'];
932 -$wgConfirmEditMessages['zh-sg'] = $wgConfirmEditMessages['zh-hans'];
933 -$wgConfirmEditMessages['zh-tw'] = $wgConfirmEditMessages['zh-hant'];
934 -$wgConfirmEditMessages['zh-yue'] = $wgConfirmEditMessages['yue'];
 928+$messages['zh'] = $messages['zh-hans'];
 929+$messages['zh-cn'] = $messages['zh-hans'];
 930+$messages['zh-hk'] = $messages['zh-hant'];
 931+$messages['zh-min-nan'] = $messages['nan'];
 932+$messages['zh-sg'] = $messages['zh-hans'];
 933+$messages['zh-tw'] = $messages['zh-hant'];
 934+$messages['zh-yue'] = $messages['yue'];
Index: trunk/extensions/ConfirmEdit/MathCaptcha.class.php
@@ -0,0 +1,40 @@
 2+<?php
 3+
 4+class MathCaptcha extends SimpleCaptcha {
 5+
 6+ /** Validate a captcha response */
 7+ function keyMatch( $req, $info ) {
 8+ return (int)$req->getVal( 'wpCaptchaAnswer' ) == (int)$info['answer'];
 9+ }
 10+
 11+ /** Produce a nice little form */
 12+ function getForm() {
 13+ list( $sum, $answer ) = $this->pickSum();
 14+ $index = $this->storeCaptcha( array( 'answer' => $answer ) );
 15+
 16+ $form = '<table><tr><td>' . $this->fetchMath( $sum ) . '</td>';
 17+ $form .= '<td>' . wfInput( 'wpCaptchaAnswer', false, false, array( 'tabindex' => '1' ) ) . '</td></tr></table>';
 18+ $form .= wfHidden( 'wpCaptchaId', $index );
 19+ return $form;
 20+ }
 21+
 22+ /** Pick a random sum */
 23+ function pickSum() {
 24+ $a = mt_rand( 0, 100 );
 25+ $b = mt_rand( 0, 10 );
 26+ $op = mt_rand( 0, 1 ) ? '+' : '-';
 27+ $sum = "{$a} {$op} {$b} = ";
 28+ $ans = $op == '+' ? ( $a + $b ) : ( $a - $b );
 29+ return array( $sum, $ans );
 30+ }
 31+
 32+ /** Fetch the math */
 33+ function fetchMath( $sum ) {
 34+ $math = new MathRenderer( $sum );
 35+ $math->setOutputMode( MW_MATH_PNG );
 36+ $html = $math->render();
 37+ return preg_replace( '/alt=".*"/', '', $html );
 38+ }
 39+
 40+}
 41+?>
Property changes on: trunk/extensions/ConfirmEdit/MathCaptcha.class.php
___________________________________________________________________
Added: svn:eol-style
142 + native
Index: trunk/extensions/ConfirmEdit/FancyCaptcha.i18n.php
@@ -5,10 +5,8 @@
66 *
77 * @addtogroup Extensions
88 */
 9+$messages = array(
910
10 -function efFancyCaptchaMessages() {
11 - $messages = array(
12 -
1311 /* English */
1412 'en' => array(
1513 'fancycaptcha-addurl' => 'Your edit includes new external links. To help protect against automated
@@ -350,18 +348,16 @@
351349 'fancycaptcha-edit' => '如您想要編輯此頁面,請輸入以下的文字([[Special:Captcha/help|相關資訊]]):',
352350 ),
353351
354 - );
 352+);
355353
356 - /* Kazakh default, fallback to kk-kz */
357 - $messages['kk'] = $messages['kk-kz'];
358 - /* Chinese defaults, fallback to zh-hans or zh-hant */
359 - $messages['zh'] = $messages['zh-hans'];
360 - $messages['zh-cn'] = $messages['zh-hans'];
361 - $messages['zh-hk'] = $messages['zh-hant'];
362 - $messages['zh-tw'] = $messages['zh-hans'];
363 - $messages['zh-sg'] = $messages['zh-hant'];
364 - /* Cantonese default, fallback to yue */
365 - $messages['zh-yue'] = $messages['yue'];
 354+/* Kazakh default, fallback to kk-kz */
 355+$messages['kk'] = $messages['kk-kz'];
 356+/* Chinese defaults, fallback to zh-hans or zh-hant */
 357+$messages['zh'] = $messages['zh-hans'];
 358+$messages['zh-cn'] = $messages['zh-hans'];
 359+$messages['zh-hk'] = $messages['zh-hant'];
 360+$messages['zh-tw'] = $messages['zh-hans'];
 361+$messages['zh-sg'] = $messages['zh-hant'];
 362+/* Cantonese default, fallback to yue */
 363+$messages['zh-yue'] = $messages['yue'];
366364
367 - return $messages;
368 -}
Index: trunk/extensions/ConfirmEdit/ConfirmEdit.php
@@ -27,11 +27,13 @@
2828 * @addtogroup Extensions
2929 */
3030
31 -if ( defined( 'MEDIAWIKI' ) ) {
 31+if ( !defined( 'MEDIAWIKI' ) ) {
 32+ exit;
 33+}
3234
3335 global $wgExtensionFunctions, $wgGroupPermissions;
3436
35 -$wgExtensionFunctions[] = 'ceSetup';
 37+$wgExtensionFunctions[] = 'confirmEditSetup';
3638 $wgExtensionCredits['other'][] = array(
3739 'name' => 'ConfirmEdit',
3840 'author' => 'Brion Vibber',
@@ -39,9 +41,6 @@
4042 'description' => 'Simple captcha implementation',
4143 );
4244
43 -# Internationalisation file
44 -require_once( 'ConfirmEdit.i18n.php' );
45 -
4645 /**
4746 * The 'skipcaptcha' permission key can be given out to
4847 * let known-good users perform triggering actions without
@@ -167,29 +166,33 @@
168167
169168 /** Register special page */
170169 global $wgSpecialPages;
171 -$wgSpecialPages['Captcha'] = array( /*class*/ 'SpecialPage', /*name*/'Captcha', /*restriction*/ '',
172 - /*listed*/ false, /*function*/ false, /*file*/ false );
 170+$wgSpecialPages['Captcha'] = array( /*class*/'CaptchaSpecialPage', /*name*/'Captcha' );
173171
174 -/**
175 - * Set up message strings for captcha utilities.
176 - */
177 -function ceSetup() {
178 - # Add messages
179 - global $wgMessageCache, $wgConfirmEditMessages;
180 - foreach( $wgConfirmEditMessages as $lang => $messages )
181 - $wgMessageCache->addMessages( $messages, $lang );
 172+$wgConfirmEditIP = dirname( __FILE__ );
 173+$wgExtensionMessagesFiles['ConfirmEdit'] = "$wgConfirmEditIP/ConfirmEdit.i18n.php";
182174
183 - global $wgHooks, $wgCaptcha, $wgCaptchaClass;
184 - $wgCaptcha = new $wgCaptchaClass();
185 - $wgHooks['EditFilter'][] = array( &$wgCaptcha, 'confirmEdit' );
 175+if ( defined( 'MW_SUPPORTS_EDITFILTERMERGED' ) ) {
 176+ $wgHooks['EditFilterMerged'][] = 'ConfirmEditHooks::confirmEditMerged';
 177+} else {
 178+ $wgHooks['EditFilter'][] = 'ConfirmEditHooks::confirmEdit';
 179+}
 180+$wgHooks['UserCreateForm'][] = 'ConfirmEditHooks::injectUserCreate';
 181+$wgHooks['AbortNewAccount'][] = 'ConfirmEditHooks::confirmUserCreate';
 182+$wgHooks['LoginAuthenticateAudit'][] = 'ConfirmEditHooks::triggerUserLogin';
 183+$wgHooks['UserLoginForm'][] = 'ConfirmEditHooks::injectUserLogin';
 184+$wgHooks['AbortLogin'][] = 'ConfirmEditHooks::confirmUserLogin';
186185
187 - $wgHooks['UserCreateForm'][] = array( &$wgCaptcha, 'injectUserCreate' );
188 - $wgHooks['AbortNewAccount'][] = array( &$wgCaptcha, 'confirmUserCreate' );
189 -
190 - $wgHooks['LoginAuthenticateAudit'][] = array( &$wgCaptcha, 'triggerUserLogin' );
191 - $wgHooks['UserLoginForm'][] = array( &$wgCaptcha, 'injectUserLogin' );
192 - $wgHooks['AbortLogin'][] = array( &$wgCaptcha, 'confirmUserLogin' );
193 -
 186+$wgAutoloadClasses['ConfirmEditHooks']
 187+ = $wgAutoloadClasses['SimpleCaptcha']
 188+ = $wgAutoloadClasses['CaptchaSessionStore']
 189+ = $wgAutoloadClasses['CaptchaCacheStore']
 190+ = $wgAutoloadClasses['CaptchaSpecialPage']
 191+ = "$wgConfirmEditIP/ConfirmEdit_body.php";
 192+
 193+/**
 194+ * Set up $wgWhitelistRead
 195+ */
 196+function confirmEditSetup() {
194197 global $wgGroupPermissions, $wgCaptchaTriggers;
195198 if( !$wgGroupPermissions['*']['read'] && $wgCaptchaTriggers['badlogin'] ) {
196199 // We need to ensure that the captcha interface is accessible
@@ -203,596 +206,4 @@
204207 }
205208 }
206209
207 -/**
208 - * Entry point for Special:Captcha
209 - */
210 -function wfSpecialCaptcha( $par = null ) {
211 - global $wgCaptcha;
212 - switch( $par ) {
213 - case "image":
214 - return $wgCaptcha->showImage();
215 - case "help":
216 - default:
217 - return $wgCaptcha->showHelp();
218 - }
219 -}
220210
221 -class SimpleCaptcha {
222 - function SimpleCaptcha() {
223 - global $wgCaptchaStorageClass;
224 - $this->storage = new $wgCaptchaStorageClass;
225 - }
226 -
227 - /**
228 - * Insert a captcha prompt into the edit form.
229 - * This sample implementation generates a simple arithmetic operation;
230 - * it would be easy to defeat by machine.
231 - *
232 - * Override this!
233 - *
234 - * @return string HTML
235 - */
236 - function getForm() {
237 - $a = mt_rand(0, 100);
238 - $b = mt_rand(0, 10);
239 - $op = mt_rand(0, 1) ? '+' : '-';
240 -
241 - $test = "$a $op $b";
242 - $answer = ($op == '+') ? ($a + $b) : ($a - $b);
243 -
244 - $index = $this->storeCaptcha( array( 'answer' => $answer ) );
245 -
246 - return "<p><label for=\"wpCaptchaWord\">$test</label> = " .
247 - wfElement( 'input', array(
248 - 'name' => 'wpCaptchaWord',
249 - 'id' => 'wpCaptchaWord',
250 - 'tabindex' => 1 ) ) . // tab in before the edit textarea
251 - "</p>\n" .
252 - wfElement( 'input', array(
253 - 'type' => 'hidden',
254 - 'name' => 'wpCaptchaId',
255 - 'id' => 'wpCaptchaId',
256 - 'value' => $index ) );
257 - }
258 -
259 - /**
260 - * Insert the captcha prompt into an edit form.
261 - * @param OutputPage $out
262 - */
263 - function editCallback( &$out ) {
264 - $out->addWikiText( $this->getMessage( $this->action ) );
265 - $out->addHTML( $this->getForm() );
266 - }
267 -
268 - /**
269 - * Show a message asking the user to enter a captcha on edit
270 - * The result will be treated as wiki text
271 - *
272 - * @param $action Action being performed
273 - * @return string
274 - */
275 - function getMessage( $action ) {
276 - $name = 'captcha-' . $action;
277 - $text = wfMsg( $name );
278 - # Obtain a more tailored message, if possible, otherwise, fall back to
279 - # the default for edits
280 - return wfEmptyMsg( $name, $text ) ? wfMsg( 'captcha-edit' ) : $text;
281 - }
282 -
283 - /**
284 - * Inject whazawhoo
285 - * @fixme if multiple thingies insert a header, could break
286 - * @param SimpleTemplate $template
287 - * @return bool true to keep running callbacks
288 - */
289 - function injectUserCreate( &$template ) {
290 - global $wgCaptchaTriggers, $wgOut;
291 - if( $wgCaptchaTriggers['createaccount'] ) {
292 - $template->set( 'header',
293 - "<div class='captcha'>" .
294 - $wgOut->parse( $this->getMessage( 'createaccount' ) ) .
295 - $this->getForm() .
296 - "</div>\n" );
297 - }
298 - return true;
299 - }
300 -
301 - /**
302 - * Inject a captcha into the user login form after a failed
303 - * password attempt as a speedbump for mass attacks.
304 - * @fixme if multiple thingies insert a header, could break
305 - * @param SimpleTemplate $template
306 - * @return bool true to keep running callbacks
307 - */
308 - function injectUserLogin( &$template ) {
309 - if( $this->isBadLoginTriggered() ) {
310 - global $wgOut;
311 - $template->set( 'header',
312 - "<div class='captcha'>" .
313 - $wgOut->parse( $this->getMessage( 'badlogin' ) ) .
314 - $this->getForm() .
315 - "</div>\n" );
316 - }
317 - return true;
318 - }
319 -
320 - /**
321 - * When a bad login attempt is made, increment an expiring counter
322 - * in the memcache cloud. Later checks for this may trigger a
323 - * captcha display to prevent too many hits from the same place.
324 - * @param User $user
325 - * @param string $password
326 - * @param int $retval authentication return value
327 - * @return bool true to keep running callbacks
328 - */
329 - function triggerUserLogin( $user, $password, $retval ) {
330 - global $wgCaptchaTriggers, $wgCaptchaBadLoginExpiration, $wgMemc;
331 - if( $retval == LoginForm::WRONG_PASS && $wgCaptchaTriggers['badlogin'] ) {
332 - $key = $this->badLoginKey();
333 - $count = $wgMemc->get( $key );
334 - if( !$count ) {
335 - $wgMemc->add( $key, 0, $wgCaptchaBadLoginExpiration );
336 - }
337 - $count = $wgMemc->incr( $key );
338 - }
339 - return true;
340 - }
341 -
342 - /**
343 - * Check if a bad login has already been registered for this
344 - * IP address. If so, require a captcha.
345 - * @return bool
346 - * @access private
347 - */
348 - function isBadLoginTriggered() {
349 - global $wgMemc;
350 - return intval( $wgMemc->get( $this->badLoginKey() ) ) > 0;
351 - }
352 -
353 - /**
354 - * Internal cache key for badlogin checks.
355 - * @return string
356 - * @access private
357 - */
358 - function badLoginKey() {
359 - return wfMemcKey( 'captcha', 'badlogin', 'ip', wfGetIP() );
360 - }
361 -
362 - /**
363 - * Check if the submitted form matches the captcha session data provided
364 - * by the plugin when the form was generated.
365 - *
366 - * Override this!
367 - *
368 - * @param WebRequest $request
369 - * @param array $info
370 - * @return bool
371 - */
372 - function keyMatch( $request, $info ) {
373 - return $request->getVal( 'wpCaptchaWord' ) == $info['answer'];
374 - }
375 -
376 - // ----------------------------------
377 -
378 - /**
379 - * @param EditPage $editPage
380 - * @param string $action (edit/create/addurl...)
381 - * @return bool true if action triggers captcha on editPage's namespace
382 - */
383 - function captchaTriggers( &$editPage, $action) {
384 - global $wgCaptchaTriggers, $wgCaptchaTriggersOnNamespace;
385 - //Special config for this NS?
386 - if (isset( $wgCaptchaTriggersOnNamespace[$editPage->mTitle->getNamespace()][$action] ) )
387 - return $wgCaptchaTriggersOnNamespace[$editPage->mTitle->getNamespace()][$action];
388 -
389 - return ( !empty( $wgCaptchaTriggers[$action] ) ); //Default
390 - }
391 -
392 -
393 - /**
394 - * @param EditPage $editPage
395 - * @param string $newtext
396 - * @param string $section
397 - * @return bool true if the captcha should run
398 - */
399 - function shouldCheck( &$editPage, $newtext, $section ) {
400 - $this->trigger = '';
401 -
402 - global $wgUser;
403 - if( $wgUser->isAllowed( 'skipcaptcha' ) ) {
404 - wfDebug( "ConfirmEdit: user group allows skipping captcha\n" );
405 - return false;
406 - }
407 - global $wgCaptchaWhitelistIP;
408 - if( !empty( $wgCaptchaWhitelistIP ) ) {
409 - $ip = wfGetIp();
410 - foreach ( $wgCaptchaWhitelistIP as $range ) {
411 - if ( IP::isInRange( $ip, $range ) ) {
412 - return false;
413 - }
414 - }
415 - }
416 -
417 -
418 - global $wgEmailAuthentication, $ceAllowConfirmedEmail;
419 - if( $wgEmailAuthentication && $ceAllowConfirmedEmail &&
420 - $wgUser->isEmailConfirmed() ) {
421 - wfDebug( "ConfirmEdit: user has confirmed mail, skipping captcha\n" );
422 - return false;
423 - }
424 -
425 - if( $this->captchaTriggers( $editPage, 'edit' ) ) {
426 - // Check on all edits
427 - global $wgUser, $wgTitle;
428 - $this->trigger = sprintf( "edit trigger by '%s' at [[%s]]",
429 - $wgUser->getName(),
430 - $wgTitle->getPrefixedText() );
431 - $this->action = 'edit';
432 - wfDebug( "ConfirmEdit: checking all edits...\n" );
433 - return true;
434 - }
435 -
436 - if( $this->captchaTriggers( $editPage, 'create' ) && !$editPage->mTitle->exists() ) {
437 - //Check if creating a page
438 - global $wgUser, $wgTitle;
439 - $this->trigger = sprintf( "Create trigger by '%s' at [[%s]]",
440 - $wgUser->getName(),
441 - $wgTitle->getPrefixedText() );
442 - $this->action = 'create';
443 - wfDebug( "ConfirmEdit: checking on page creation...\n" );
444 - return true;
445 - }
446 -
447 - if( $this->captchaTriggers( $editPage, 'addurl' ) ) {
448 - // Only check edits that add URLs
449 - $oldtext = $this->loadText( $editPage, $section );
450 -
451 - $oldLinks = $this->findLinks( $oldtext );
452 - $newLinks = $this->findLinks( $newtext );
453 - $unknownLinks = array_filter( $newLinks, array( &$this, 'filterLink' ) );
454 -
455 - $addedLinks = array_diff( $unknownLinks, $oldLinks );
456 - $numLinks = count( $addedLinks );
457 -
458 - if( $numLinks > 0 ) {
459 - global $wgUser, $wgTitle;
460 - $this->trigger = sprintf( "%dx url trigger by '%s' at [[%s]]: %s",
461 - $numLinks,
462 - $wgUser->getName(),
463 - $wgTitle->getPrefixedText(),
464 - implode( ", ", $addedLinks ) );
465 - $this->action = 'addurl';
466 - return true;
467 - }
468 - }
469 -
470 - global $wgCaptchaRegexes;
471 - if( !empty( $wgCaptchaRegexes ) ) {
472 - // Custom regex checks
473 - $oldtext = $this->loadText( $editPage, $section );
474 -
475 - foreach( $wgCaptchaRegexes as $regex ) {
476 - $newMatches = array();
477 - if( preg_match_all( $regex, $newtext, $newMatches ) ) {
478 - $oldMatches = array();
479 - preg_match_all( $regex, $oldtext, $oldMatches );
480 -
481 - $addedMatches = array_diff( $newMatches[0], $oldMatches[0] );
482 -
483 - $numHits = count( $addedMatches );
484 - if( $numHits > 0 ) {
485 - global $wgUser, $wgTitle;
486 - $this->trigger = sprintf( "%dx %s at [[%s]]: %s",
487 - $numHits,
488 - $regex,
489 - $wgUser->getName(),
490 - $wgTitle->getPrefixedText(),
491 - implode( ", ", $addedMatches ) );
492 - $this->action = 'edit';
493 - return true;
494 - }
495 - }
496 - }
497 - }
498 -
499 - return false;
500 - }
501 -
502 - /**
503 - * Filter callback function for URL whitelisting
504 - * @param string url to check
505 - * @return bool true if unknown, false if whitelisted
506 - * @access private
507 - */
508 - function filterLink( $url ) {
509 - global $wgCaptchaWhitelist;
510 - $source = wfMsgForContent( 'captcha-addurl-whitelist' );
511 -
512 - $whitelist = wfEmptyMsg( 'captcha-addurl-whitelist', $source )
513 - ? false
514 - : $this->buildRegexes( explode( "\n", $source ) );
515 -
516 - $cwl = $wgCaptchaWhitelist !== false ? preg_match( $wgCaptchaWhitelist, $url ) : false;
517 - $wl = $whitelist !== false ? preg_match( $whitelist, $url ) : false;
518 -
519 - return !( $cwl || $wl );
520 - }
521 -
522 - /**
523 - * Build regex from whitelist
524 - * @param string lines from [[MediaWiki:Captcha-addurl-whitelist]]
525 - * @return string Regex or bool false if whitelist is empty
526 - * @access private
527 - */
528 - function buildRegexes( $lines ) {
529 - # Code duplicated from the SpamBlacklist extension (r19197)
530 -
531 - # Strip comments and whitespace, then remove blanks
532 - $lines = array_filter( array_map( 'trim', preg_replace( '/#.*$/', '', $lines ) ) );
533 -
534 - # No lines, don't make a regex which will match everything
535 - if ( count( $lines ) == 0 ) {
536 - wfDebug( "No lines\n" );
537 - return false;
538 - } else {
539 - # Make regex
540 - # It's faster using the S modifier even though it will usually only be run once
541 - //$regex = 'http://+[a-z0-9_\-.]*(' . implode( '|', $lines ) . ')';
542 - //return '/' . str_replace( '/', '\/', preg_replace('|\\\*/|', '/', $regex) ) . '/Si';
543 - $regexes = '';
544 - $regexStart = '/http:\/\/+[a-z0-9_\-.]*(';
545 - $regexEnd = ')/Si';
546 - $regexMax = 4096;
547 - $build = false;
548 - foreach( $lines as $line ) {
549 - // FIXME: not very robust size check, but should work. :)
550 - if( $build === false ) {
551 - $build = $line;
552 - } elseif( strlen( $build ) + strlen( $line ) > $regexMax ) {
553 - $regexes .= $regexStart .
554 - str_replace( '/', '\/', preg_replace('|\\\*/|', '/', $build) ) .
555 - $regexEnd;
556 - $build = $line;
557 - } else {
558 - $build .= '|' . $line;
559 - }
560 - }
561 - if( $build !== false ) {
562 - $regexes .= $regexStart .
563 - str_replace( '/', '\/', preg_replace('|\\\*/|', '/', $build) ) .
564 - $regexEnd;
565 - }
566 - return $regexes;
567 - }
568 - }
569 -
570 - /**
571 - * The main callback run on edit attempts.
572 - * @param EditPage $editPage
573 - * @param string $newtext
574 - * @param string $section
575 - * @param bool true to continue saving, false to abort and show a captcha form
576 - */
577 - function confirmEdit( &$editPage, $newtext, $section ) {
578 - if( $this->shouldCheck( $editPage, $newtext, $section ) ) {
579 - if( $this->passCaptcha() ) {
580 - return true;
581 - } else {
582 - $editPage->showEditForm( array( &$this, 'editCallback' ) );
583 - return false;
584 - }
585 - } else {
586 - wfDebug( "ConfirmEdit: no need to show captcha.\n" );
587 - return true;
588 - }
589 - }
590 -
591 - /**
592 - * Hook for user creation form submissions.
593 - * @param User $u
594 - * @param string $message
595 - * @return bool true to continue, false to abort user creation
596 - */
597 - function confirmUserCreate( $u, &$message ) {
598 - global $wgCaptchaTriggers;
599 - if( $wgCaptchaTriggers['createaccount'] ) {
600 - $this->trigger = "new account '" . $u->getName() . "'";
601 - if( !$this->passCaptcha() ) {
602 - $message = wfMsg( 'captcha-createaccount-fail' );
603 - return false;
604 - }
605 - }
606 - return true;
607 - }
608 -
609 - /**
610 - * Hook for user login form submissions.
611 - * @param User $u
612 - * @param string $message
613 - * @return bool true to continue, false to abort user creation
614 - */
615 - function confirmUserLogin( $u, $pass, &$retval ) {
616 - if( $this->isBadLoginTriggered() ) {
617 - $this->trigger = "post-badlogin login '" . $u->getName() . "'";
618 - if( !$this->passCaptcha() ) {
619 - $message = wfMsg( 'captcha-badlogin-fail' );
620 - // Emulate a bad-password return to confuse the shit out of attackers
621 - $retval = LoginForm::WRONG_PASS;
622 - return false;
623 - }
624 - }
625 - return true;
626 - }
627 -
628 - /**
629 - * Given a required captcha run, test form input for correct
630 - * input on the open session.
631 - * @return bool if passed, false if failed or new session
632 - */
633 - function passCaptcha() {
634 - $info = $this->retrieveCaptcha();
635 - if( $info ) {
636 - global $wgRequest;
637 - if( $this->keyMatch( $wgRequest, $info ) ) {
638 - $this->log( "passed" );
639 - $this->clearCaptcha( $info );
640 - return true;
641 - } else {
642 - $this->clearCaptcha( $info );
643 - $this->log( "bad form input" );
644 - return false;
645 - }
646 - } else {
647 - $this->log( "new captcha session" );
648 - return false;
649 - }
650 - }
651 -
652 - /**
653 - * Log the status and any triggering info for debugging or statistics
654 - * @param string $message
655 - */
656 - function log( $message ) {
657 - wfDebugLog( 'captcha', 'ConfirmEdit: ' . $message . '; ' . $this->trigger );
658 - }
659 -
660 - /**
661 - * Generate a captcha session ID and save the info in PHP's session storage.
662 - * (Requires the user to have cookies enabled to get through the captcha.)
663 - *
664 - * A random ID is used so legit users can make edits in multiple tabs or
665 - * windows without being unnecessarily hobbled by a serial order requirement.
666 - * Pass the returned id value into the edit form as wpCaptchaId.
667 - *
668 - * @param array $info data to store
669 - * @return string captcha ID key
670 - */
671 - function storeCaptcha( $info ) {
672 - if( !isset( $info['index'] ) ) {
673 - // Assign random index if we're not udpating
674 - $info['index'] = strval( mt_rand() );
675 - }
676 - $this->storage->store( $info['index'], $info );
677 - return $info['index'];
678 - }
679 -
680 - /**
681 - * Fetch this session's captcha info.
682 - * @return mixed array of info, or false if missing
683 - */
684 - function retrieveCaptcha() {
685 - global $wgRequest;
686 - $index = $wgRequest->getVal( 'wpCaptchaId' );
687 - return $this->storage->retrieve( $index );
688 - }
689 -
690 - /**
691 - * Clear out existing captcha info from the session, to ensure
692 - * it can't be reused.
693 - */
694 - function clearCaptcha( $info ) {
695 - $this->storage->clear( $info['index'] );
696 - }
697 -
698 - /**
699 - * Retrieve the current version of the page or section being edited...
700 - * @param EditPage $editPage
701 - * @param string $section
702 - * @return string
703 - * @access private
704 - */
705 - function loadText( $editPage, $section ) {
706 - $rev = Revision::newFromTitle( $editPage->mTitle );
707 - if( is_null( $rev ) ) {
708 - return "";
709 - } else {
710 - $text = $rev->getText();
711 - if( $section != '' ) {
712 - return Article::getSection( $text, $section );
713 - } else {
714 - return $text;
715 - }
716 - }
717 - }
718 -
719 - /**
720 - * Extract a list of all recognized HTTP links in the text.
721 - * @param string $text
722 - * @return array of strings
723 - */
724 - function findLinks( $text ) {
725 - global $wgParser, $wgTitle, $wgUser;
726 -
727 - $options = new ParserOptions();
728 - $text = $wgParser->preSaveTransform( $text, $wgTitle, $wgUser, $options );
729 - $out = $wgParser->parse( $text, $wgTitle, $options );
730 -
731 - return array_keys( $out->getExternalLinks() );
732 - }
733 -
734 - /**
735 - * Show a page explaining what this wacky thing is.
736 - */
737 - function showHelp() {
738 - global $wgOut, $ceAllowConfirmedEmail;
739 - $wgOut->setPageTitle( wfMsg( 'captchahelp-title' ) );
740 - $wgOut->addWikiText( wfMsg( 'captchahelp-text' ) );
741 - if ( $this->storage->cookiesNeeded() ) {
742 - $wgOut->addWikiText( wfMsg( 'captchahelp-cookies-needed' ) );
743 - }
744 - }
745 -
746 -}
747 -
748 -class CaptchaSessionStore {
749 - function store( $index, $info ) {
750 - $_SESSION['captcha' . $info['index']] = $info;
751 - }
752 -
753 - function retrieve( $index ) {
754 - if( isset( $_SESSION['captcha' . $index] ) ) {
755 - return $_SESSION['captcha' . $index];
756 - } else {
757 - return false;
758 - }
759 - }
760 -
761 - function clear( $index ) {
762 - unset( $_SESSION['captcha' . $index] );
763 - }
764 -
765 - function cookiesNeeded() {
766 - return true;
767 - }
768 -}
769 -
770 -class CaptchaCacheStore {
771 - function store( $index, $info ) {
772 - global $wgMemc, $wgCaptchaSessionExpiration;
773 - $wgMemc->set( wfMemcKey( 'captcha', $index ), $info,
774 - $wgCaptchaSessionExpiration );
775 - }
776 -
777 - function retrieve( $index ) {
778 - global $wgMemc;
779 - $info = $wgMemc->get( wfMemcKey( 'captcha', $index ) );
780 - if( $info ) {
781 - return $info;
782 - } else {
783 - return false;
784 - }
785 - }
786 -
787 - function clear( $index ) {
788 - global $wgMemc;
789 - $wgMemc->delete( wfMemcKey( 'captcha', $index ) );
790 - }
791 -
792 - function cookiesNeeded() {
793 - return false;
794 - }
795 -}
796 -
797 -} # End invocation guard
798 -
799 -
Index: trunk/extensions/ConfirmEdit/FancyCaptcha.php
@@ -24,7 +24,9 @@
2525 * @addtogroup Extensions
2626 */
2727
28 -if ( defined( 'MEDIAWIKI' ) ) {
 28+if ( !defined( 'MEDIAWIKI' ) ) {
 29+ exit;
 30+}
2931
3032 global $wgCaptchaDirectory;
3133 $wgCaptchaDirectory = "$wgUploadDirectory/captcha"; // bad default :D
@@ -35,226 +37,6 @@
3638 global $wgCaptchaSecret;
3739 $wgCaptchaSecret = "CHANGE_THIS_SECRET!";
3840
39 -$wgExtensionFunctions[] = 'efFancyCaptcha';
 41+$wgExtensionMessagesFiles['FancyCaptcha'] = dirname(__FILE__).'/FancyCaptcha.i18n.php';
 42+$wgAutoloadClasses['FancyCaptcha'] = dirname( __FILE__ ) . '/FancyCaptcha.class.php';
4043
41 -function efFancyCaptcha() {
42 - global $wgMessageCache;
43 - require_once( dirname( __FILE__ ) . '/FancyCaptcha.i18n.php' );
44 - foreach( efFancyCaptchaMessages() as $lang => $messages )
45 - $wgMessageCache->addMessages( $messages, $lang );
46 -}
47 -
48 -class FancyCaptcha extends SimpleCaptcha {
49 - /**
50 - * Check if the submitted form matches the captcha session data provided
51 - * by the plugin when the form was generated.
52 - *
53 - * @param WebRequest $request
54 - * @param array $info
55 - * @return bool
56 - */
57 - function keyMatch( $request, $info ) {
58 - global $wgCaptchaSecret;
59 -
60 - $answer = $request->getVal( 'wpCaptchaWord' );
61 - $digest = $wgCaptchaSecret . $info['salt'] . $answer . $wgCaptchaSecret . $info['salt'];
62 - $answerHash = substr( md5( $digest ), 0, 16 );
63 -
64 - if( $answerHash == $info['hash'] ) {
65 - wfDebug( "FancyCaptcha: answer hash matches expected {$info['hash']}\n" );
66 - return true;
67 - } else {
68 - wfDebug( "FancyCaptcha: answer hashes to $answerHash, expected {$info['hash']}\n" );
69 - return false;
70 - }
71 - }
72 -
73 - /**
74 - * Insert the captcha prompt into the edit form.
75 - */
76 - function getForm() {
77 - $info = $this->pickImage();
78 - if( !$info ) {
79 - die( "out of captcha images; this shouldn't happen" );
80 - }
81 -
82 - // Generate a random key for use of this captcha image in this session.
83 - // This is needed so multiple edits in separate tabs or windows can
84 - // go through without extra pain.
85 - $index = $this->storeCaptcha( $info );
86 -
87 - wfDebug( "Captcha id $index using hash ${info['hash']}, salt ${info['salt']}.\n" );
88 -
89 - $title = Title::makeTitle( NS_SPECIAL, 'Captcha/image' );
90 -
91 - return "<p>" .
92 - wfElement( 'img', array(
93 - 'src' => $title->getLocalUrl( 'wpCaptchaId=' . urlencode( $index ) ),
94 - 'width' => $info['width'],
95 - 'height' => $info['height'],
96 - 'alt' => '' ) ) .
97 - "</p>\n" .
98 - wfElement( 'input', array(
99 - 'type' => 'hidden',
100 - 'name' => 'wpCaptchaId',
101 - 'id' => 'wpCaptchaId',
102 - 'value' => $index ) ) .
103 - "<p>" .
104 - wfElement( 'input', array(
105 - 'name' => 'wpCaptchaWord',
106 - 'id' => 'wpCaptchaWord',
107 - 'tabindex' => 1 ) ) . // tab in before the edit textarea
108 - "</p>\n";
109 - }
110 -
111 - /**
112 - * Select a previously generated captcha image from the queue.
113 - * @fixme subject to race conditions if lots of files vanish
114 - * @return mixed tuple of (salt key, text hash) or false if no image to find
115 - */
116 - function pickImage() {
117 - global $wgCaptchaDirectory, $wgCaptchaDirectoryLevels;
118 - return $this->pickImageDir(
119 - $wgCaptchaDirectory,
120 - $wgCaptchaDirectoryLevels );
121 - }
122 -
123 - function pickImageDir( $directory, $levels ) {
124 - if( $levels ) {
125 - $dirs = array();
126 -
127 - // Check which subdirs are actually present...
128 - $dir = opendir( $directory );
129 - while( false !== ($entry = readdir( $dir ) ) ) {
130 - if( ctype_xdigit( $entry ) && strlen( $entry ) == 1 ) {
131 - $dirs[] = $entry;
132 - }
133 - }
134 - closedir( $dir );
135 -
136 - $place = mt_rand( 0, count( $dirs ) - 1 );
137 - // In case all dirs are not filled,
138 - // cycle through next digits...
139 - for( $j = 0; $j < count( $dirs ); $j++ ) {
140 - $char = $dirs[($place + $j) % count( $dirs )];
141 - $return = $this->pickImageDir( "$directory/$char", $levels - 1 );
142 - if( $return ) {
143 - return $return;
144 - }
145 - }
146 - // Didn't find any images in this directory... empty?
147 - return false;
148 - } else {
149 - return $this->pickImageFromDir( $directory );
150 - }
151 - }
152 -
153 - function pickImageFromDir( $directory ) {
154 - if( !is_dir( $directory ) ) {
155 - return false;
156 - }
157 - $n = mt_rand( 0, $this->countFiles( $directory ) - 1 );
158 - $dir = opendir( $directory );
159 -
160 - $count = 0;
161 -
162 - $entry = readdir( $dir );
163 - $pick = false;
164 - while( false !== $entry ) {
165 - $entry = readdir( $dir );
166 - if( preg_match( '/^image_([0-9a-f]+)_([0-9a-f]+)\\.png$/', $entry, $matches ) ) {
167 - $size = getimagesize( "$directory/$entry" );
168 - $pick = array(
169 - 'salt' => $matches[1],
170 - 'hash' => $matches[2],
171 - 'width' => $size[0],
172 - 'height' => $size[1],
173 - 'viewed' => false,
174 - );
175 - if( $count++ == $n ) {
176 - break;
177 - }
178 - }
179 - }
180 - closedir( $dir );
181 - return $pick;
182 - }
183 -
184 - /**
185 - * Count the number of files in a directory.
186 - * @return int
187 - */
188 - function countFiles( $dirname ) {
189 - $dir = opendir( $dirname );
190 - $count = 0;
191 - while( false !== ($entry = readdir( $dir ) ) ) {
192 - if( $entry != '.' && $entry != '..' ) {
193 - $count++;
194 - }
195 - }
196 - closedir( $dir );
197 - return $count;
198 - }
199 -
200 - function showImage() {
201 - global $wgOut, $wgRequest;
202 -
203 - $wgOut->disable();
204 -
205 - $info = $this->retrieveCaptcha();
206 - if( $info ) {
207 - if( $info['viewed'] ) {
208 - wfHttpError( 403, 'Access Forbidden', "Can't view captcha image a second time." );
209 - return false;
210 - }
211 -
212 - $info['viewed'] = wfTimestamp();
213 - $this->storeCaptcha( $info );
214 -
215 - $salt = $info['salt'];
216 - $hash = $info['hash'];
217 - $file = $this->imagePath( $salt, $hash );
218 -
219 - if( file_exists( $file ) ) {
220 - global $IP;
221 - require_once "$IP/includes/StreamFile.php";
222 - wfStreamFile( $file );
223 - return true;
224 - }
225 - }
226 - wfHttpError( 500, 'Internal Error', 'Requested bogus captcha image' );
227 - return false;
228 - }
229 -
230 - function imagePath( $salt, $hash ) {
231 - global $wgCaptchaDirectory, $wgCaptchaDirectoryLevels;
232 - $file = $wgCaptchaDirectory;
233 - $file .= DIRECTORY_SEPARATOR;
234 - for( $i = 0; $i < $wgCaptchaDirectoryLevels; $i++ ) {
235 - $file .= $hash{$i};
236 - $file .= DIRECTORY_SEPARATOR;
237 - }
238 - $file .= "image_{$salt}_{$hash}.png";
239 - return $file;
240 - }
241 -
242 - /**
243 - * Show a message asking the user to enter a captcha on edit
244 - * The result will be treated as wiki text
245 - *
246 - * @param $action Action being performed
247 - * @return string
248 - */
249 - function getMessage( $action ) {
250 - $name = 'fancycaptcha-' . $action;
251 - $text = wfMsg( $name );
252 - # Obtain a more tailored message, if possible, otherwise, fall back to
253 - # the default for edits
254 - return wfEmptyMsg( $name, $text ) ? wfMsg( 'fancycaptcha-edit' ) : $text;
255 - }
256 -
257 -}
258 -
259 -} # End invocation guard
260 -
261 -
Index: trunk/extensions/ConfirmEdit/FancyCaptcha.class.php
@@ -0,0 +1,212 @@
 2+<?php
 3+
 4+class FancyCaptcha extends SimpleCaptcha {
 5+ /**
 6+ * Check if the submitted form matches the captcha session data provided
 7+ * by the plugin when the form was generated.
 8+ *
 9+ * @param WebRequest $request
 10+ * @param array $info
 11+ * @return bool
 12+ */
 13+ function keyMatch( $request, $info ) {
 14+ global $wgCaptchaSecret;
 15+
 16+ $answer = $request->getVal( 'wpCaptchaWord' );
 17+ $digest = $wgCaptchaSecret . $info['salt'] . $answer . $wgCaptchaSecret . $info['salt'];
 18+ $answerHash = substr( md5( $digest ), 0, 16 );
 19+
 20+ if( $answerHash == $info['hash'] ) {
 21+ wfDebug( "FancyCaptcha: answer hash matches expected {$info['hash']}\n" );
 22+ return true;
 23+ } else {
 24+ wfDebug( "FancyCaptcha: answer hashes to $answerHash, expected {$info['hash']}\n" );
 25+ return false;
 26+ }
 27+ }
 28+
 29+ /**
 30+ * Insert the captcha prompt into the edit form.
 31+ */
 32+ function getForm() {
 33+ $info = $this->pickImage();
 34+ if( !$info ) {
 35+ die( "out of captcha images; this shouldn't happen" );
 36+ }
 37+
 38+ // Generate a random key for use of this captcha image in this session.
 39+ // This is needed so multiple edits in separate tabs or windows can
 40+ // go through without extra pain.
 41+ $index = $this->storeCaptcha( $info );
 42+
 43+ wfDebug( "Captcha id $index using hash ${info['hash']}, salt ${info['salt']}.\n" );
 44+
 45+ $title = Title::makeTitle( NS_SPECIAL, 'Captcha/image' );
 46+
 47+ return "<p>" .
 48+ wfElement( 'img', array(
 49+ 'src' => $title->getLocalUrl( 'wpCaptchaId=' . urlencode( $index ) ),
 50+ 'width' => $info['width'],
 51+ 'height' => $info['height'],
 52+ 'alt' => '' ) ) .
 53+ "</p>\n" .
 54+ wfElement( 'input', array(
 55+ 'type' => 'hidden',
 56+ 'name' => 'wpCaptchaId',
 57+ 'id' => 'wpCaptchaId',
 58+ 'value' => $index ) ) .
 59+ "<p>" .
 60+ wfElement( 'input', array(
 61+ 'name' => 'wpCaptchaWord',
 62+ 'id' => 'wpCaptchaWord',
 63+ 'tabindex' => 1 ) ) . // tab in before the edit textarea
 64+ "</p>\n";
 65+ }
 66+
 67+ /**
 68+ * Select a previously generated captcha image from the queue.
 69+ * @fixme subject to race conditions if lots of files vanish
 70+ * @return mixed tuple of (salt key, text hash) or false if no image to find
 71+ */
 72+ function pickImage() {
 73+ global $wgCaptchaDirectory, $wgCaptchaDirectoryLevels;
 74+ return $this->pickImageDir(
 75+ $wgCaptchaDirectory,
 76+ $wgCaptchaDirectoryLevels );
 77+ }
 78+
 79+ function pickImageDir( $directory, $levels ) {
 80+ if( $levels ) {
 81+ $dirs = array();
 82+
 83+ // Check which subdirs are actually present...
 84+ $dir = opendir( $directory );
 85+ while( false !== ($entry = readdir( $dir ) ) ) {
 86+ if( ctype_xdigit( $entry ) && strlen( $entry ) == 1 ) {
 87+ $dirs[] = $entry;
 88+ }
 89+ }
 90+ closedir( $dir );
 91+
 92+ $place = mt_rand( 0, count( $dirs ) - 1 );
 93+ // In case all dirs are not filled,
 94+ // cycle through next digits...
 95+ for( $j = 0; $j < count( $dirs ); $j++ ) {
 96+ $char = $dirs[($place + $j) % count( $dirs )];
 97+ $return = $this->pickImageDir( "$directory/$char", $levels - 1 );
 98+ if( $return ) {
 99+ return $return;
 100+ }
 101+ }
 102+ // Didn't find any images in this directory... empty?
 103+ return false;
 104+ } else {
 105+ return $this->pickImageFromDir( $directory );
 106+ }
 107+ }
 108+
 109+ function pickImageFromDir( $directory ) {
 110+ if( !is_dir( $directory ) ) {
 111+ return false;
 112+ }
 113+ $n = mt_rand( 0, $this->countFiles( $directory ) - 1 );
 114+ $dir = opendir( $directory );
 115+
 116+ $count = 0;
 117+
 118+ $entry = readdir( $dir );
 119+ $pick = false;
 120+ while( false !== $entry ) {
 121+ $entry = readdir( $dir );
 122+ if( preg_match( '/^image_([0-9a-f]+)_([0-9a-f]+)\\.png$/', $entry, $matches ) ) {
 123+ $size = getimagesize( "$directory/$entry" );
 124+ $pick = array(
 125+ 'salt' => $matches[1],
 126+ 'hash' => $matches[2],
 127+ 'width' => $size[0],
 128+ 'height' => $size[1],
 129+ 'viewed' => false,
 130+ );
 131+ if( $count++ == $n ) {
 132+ break;
 133+ }
 134+ }
 135+ }
 136+ closedir( $dir );
 137+ return $pick;
 138+ }
 139+
 140+ /**
 141+ * Count the number of files in a directory.
 142+ * @return int
 143+ */
 144+ function countFiles( $dirname ) {
 145+ $dir = opendir( $dirname );
 146+ $count = 0;
 147+ while( false !== ($entry = readdir( $dir ) ) ) {
 148+ if( $entry != '.' && $entry != '..' ) {
 149+ $count++;
 150+ }
 151+ }
 152+ closedir( $dir );
 153+ return $count;
 154+ }
 155+
 156+ function showImage() {
 157+ global $wgOut, $wgRequest;
 158+
 159+ $wgOut->disable();
 160+
 161+ $info = $this->retrieveCaptcha();
 162+ if( $info ) {
 163+ if( $info['viewed'] ) {
 164+ wfHttpError( 403, 'Access Forbidden', "Can't view captcha image a second time." );
 165+ return false;
 166+ }
 167+
 168+ $info['viewed'] = wfTimestamp();
 169+ $this->storeCaptcha( $info );
 170+
 171+ $salt = $info['salt'];
 172+ $hash = $info['hash'];
 173+ $file = $this->imagePath( $salt, $hash );
 174+
 175+ if( file_exists( $file ) ) {
 176+ global $IP;
 177+ require_once "$IP/includes/StreamFile.php";
 178+ wfStreamFile( $file );
 179+ return true;
 180+ }
 181+ }
 182+ wfHttpError( 500, 'Internal Error', 'Requested bogus captcha image' );
 183+ return false;
 184+ }
 185+
 186+ function imagePath( $salt, $hash ) {
 187+ global $wgCaptchaDirectory, $wgCaptchaDirectoryLevels;
 188+ $file = $wgCaptchaDirectory;
 189+ $file .= DIRECTORY_SEPARATOR;
 190+ for( $i = 0; $i < $wgCaptchaDirectoryLevels; $i++ ) {
 191+ $file .= $hash{$i};
 192+ $file .= DIRECTORY_SEPARATOR;
 193+ }
 194+ $file .= "image_{$salt}_{$hash}.png";
 195+ return $file;
 196+ }
 197+
 198+ /**
 199+ * Show a message asking the user to enter a captcha on edit
 200+ * The result will be treated as wiki text
 201+ *
 202+ * @param $action Action being performed
 203+ * @return string
 204+ */
 205+ function getMessage( $action ) {
 206+ $name = 'fancycaptcha-' . $action;
 207+ $text = wfMsg( $name );
 208+ # Obtain a more tailored message, if possible, otherwise, fall back to
 209+ # the default for edits
 210+ return wfEmptyMsg( $name, $text ) ? wfMsg( 'fancycaptcha-edit' ) : $text;
 211+ }
 212+
 213+}
Property changes on: trunk/extensions/ConfirmEdit/FancyCaptcha.class.php
___________________________________________________________________
Added: svn:eol-style
1214 + native

Status & tagging log