3 # Prelim in-progress code. Proof of concept for framework, not
4 # intended as a real production captcha system!
6 # Loader for simple captcha feature
7 # Include this from LocalSettings.php
9 if ( defined( 'MEDIAWIKI' ) ) {
11 global $wgExtensionFunctions, $wgGroupPermissions;
13 $wgExtensionFunctions[] = 'ceSetup';
15 $wgGroupPermissions['*' ]['skipcaptcha'] = false;
16 $wgGroupPermissions['user' ]['skipcaptcha'] = false;
17 $wgGroupPermissions['autoconfirmed']['skipcaptcha'] = false;
18 $wgGroupPermissions['bot' ]['skipcaptcha'] = true; // registered bots
19 $wgGroupPermissions['sysop' ]['skipcaptcha'] = true;
21 global $wgCaptcha, $wgCaptchaClass, $wgCaptchaTriggers;
23 $wgCaptchaClass = 'SimpleCaptcha';
25 $wgCaptchaTriggers = array();
26 $wgCaptchaTriggers['edit'] = false; // Would check on every edit
27 $wgCaptchaTriggers['addurl'] = true; // Check on edits that add URLs
30 * Allow users who have confirmed their e-mail addresses to post
31 * URL links without being harassed by the captcha.
33 global $ceAllowConfirmedEmail;
34 $ceAllowConfirmedEmail = false;
37 * Set up message strings for captcha utilities.
40 global $wgMessageCache, $wgHooks, $wgCaptcha, $wgCaptchaClass;
41 $wgMessageCache->addMessage('captcha-short', "Your edit includes new URL links; as a protection
42 against automated spam, you'll need to enter the answer to this
43 simple arithmetic test:" );
45 SpecialPage::addPage( new SpecialPage( 'Captcha', false,
46 /*listed*/ false, /*function*/ false, /*file*/ false ) );
48 $wgCaptcha = new $wgCaptchaClass();
49 $wgHooks['EditFilter'][] = array( &$wgCaptcha, 'confirmEdit' );
53 * Entry point for Special:Captcha
55 function wfSpecialCaptcha( $par = null ) {
59 return $wgCaptcha->showImage();
62 return $wgCaptcha->showHelp();
68 * @param EditPage $editPage
69 * @param string $newtext
70 * @param string $section
71 * @return bool true if the captcha should run
73 function shouldCheck( &$editPage, $newtext, $section ) {
75 if( $wgUser->isAllowed( 'skipcaptcha' ) ) {
76 wfDebug( "SimpleCaptcha: user group allows skipping captcha\n" );
80 global $wgEmailAuthentication, $ceAllowConfirmedEmail;
81 if( $wgEmailAuthentication && $ceAllowConfirmedEmail &&
82 $wgUser->isEmailConfirmed() ) {
83 wfDebug( "SimpleCaptcha: user has confirmed mail, skipping captcha\n" );
87 global $wgCaptchaTriggers;
88 if( !empty( $wgCaptchaTriggers['edit'] ) ) {
90 wfDebug( "SimpleCaptcha: checking all edits...\n" );
94 if( !empty( $wgCaptchaTriggers['addurl'] ) ) {
95 // Only check edits that add URLs
96 $oldtext = $this->loadText( $editPage, $section );
98 $oldLinks = $this->findLinks( $oldtext );
99 $newLinks = $this->findLinks( $newtext );
101 $addedLinks = array_diff( $newLinks, $oldLinks );
102 $numLinks = count( $addedLinks );
104 if( $numLinks > 0 ) {
105 wfDebug( "SimpleCaptcha: found $numLinks new links; triggered...\n" );
113 function confirmEdit( &$editPage, $newtext, $section ) {
114 if( $this->shouldCheck( $editPage, $newtext, $section ) ) {
115 if( $this->keyMatch() ) {
116 wfDebug( "ConfirmEdit given proper key from form, passing.\n" );
119 wfDebug( "ConfirmEdit missing form key, prompting.\n" );
120 $editPage->showEditForm( array( &$this, 'formCallback' ) );
124 wfDebug( "ConfirmEdit: no new links.\n" );
129 function keyMatch() {
130 if( !isset( $_SESSION['ceAnswerVar'] ) ) {
131 wfDebug( "ConfirmEdit no session captcha key set, this is new visitor.\n" );
135 return $wgRequest->getVal( $_SESSION['ceAnswerVar'] ) == $_SESSION['ceAnswer'];
138 function formCallback( &$out ) {
139 $source = 'ceSource' . mt_rand();
140 $dest = 'ceConfirm' . mt_rand();
142 $a = mt_rand(0, 100);
144 $op = mt_rand(0, 1) ? '+' : '-';
147 $answer = ($op == '+') ? ($a + $b) : ($a - $b);
148 $_SESSION['ceAnswer'] = $answer;
149 $_SESSION['ceAnswerVar'] = $dest;
152 $out->addWikiText( wfMsg( "captcha-short" ) );
153 $out->addHTML( <<<END
154 <p><span id="$source"><label for="$dest">$test</label></span> = <input name="$dest" id="$dest" /></p>
159 function loadText( $editPage, $section ) {
160 $rev = Revision::newFromTitle( $editPage->mTitle );
161 if( is_null( $rev ) ) {
164 $text = $rev->getText();
165 if( $section != '' ) {
166 return Article::getSection( $text, $section );
173 function findLinks( $text ) {
174 $regex = '/((?:' . HTTP_PROTOCOLS . ')' . EXT_LINK_URL_CLASS . '+)/';
176 if( preg_match_all( $regex, $text, $matches, PREG_PATTERN_ORDER ) ) {
183 function showHelp() {
184 global $wgOut, $ceAllowConfirmedEmail;
185 $wgOut->setPageTitle( 'Captcha help' );
186 $wgOut->addWikiText( <<<END
187 So what's this wacky captcha thing about?
189 It's your enemy. It's here to kill you. RUN WHILE YOU STILL CAN
196 } # End invocation guard