Prelim proof-of-concept testing captcha extension framework.
[toast/cookiecaptcha.git] / ConfirmEdit.php
1 <?php
2
3 # Prelim in-progress code. Proof of concept for framework, not
4 # intended as a real production captcha system!
5
6 # Loader for spam blacklist feature
7 # Include this from LocalSettings.php
8
9 if ( defined( 'MEDIAWIKI' ) ) {
10
11 global $wgExtensionFunctions, $wgHooks, $wgGroupPermissions;
12
13 $wgExtensionFunctions[] = 'ceSetup';
14
15 $wgHooks['EditFilter'][] = 'ceConfirmEditLinks';
16
17 $wgGroupPermissions['*'        ]['skipcaptcha'] = false;
18 $wgGroupPermissions['user'     ]['skipcaptcha'] = false;
19 $wgGroupPermissions['bot'      ]['skipcaptcha'] = true; // registered bots
20 $wgGroupPermissions['sysop'    ]['skipcaptcha'] = true;
21
22 /**
23  * Allow users who have confirmed their e-mail addresses to post
24  * URL links without being harassed by the captcha.
25  */
26 global $ceAllowConfirmedEmail;
27 $ceAllowConfirmedEmail = false;
28
29 /**
30  * Set up message strings for captcha utilities.
31  */
32 function ceSetup() {
33         global $wgMessageCache;
34         $wgMessageCache->addMessage('captcha-short', "Your edit includes new URL links; as a protection
35                 against automated spam, you'll need to enter the answer to this
36                 simple arithmetic test:" );
37         SpecialPage::addPage( new SpecialPage( 'Captcha', false,
38                 /*listed*/ false, /*function*/ false, /*file*/ false ) );
39 }
40
41 /**
42  * Entry point for Special:Captcha
43  */
44 function wfSpecialCaptcha( $par = null ) {
45         switch( $par ) {
46         case "image":
47                 return ceShowImage();
48         case "help":
49         default:
50                 return ceShowHelp();
51         }
52 }
53
54 function ceConfirmEditLinks( &$editPage, $newtext, $section ) {
55         $oldtext = ceLoadText( $editPage, $section );
56         
57         $oldLinks = ceFindLinks( $oldtext );
58         $newLinks = ceFindLinks( $newtext );
59         
60         $addedLinks = array_diff( $newLinks, $oldLinks );
61         $numLinks = count( $addedLinks );
62         
63         /*
64         var_dump( $oldtext );
65         var_dump( $newtext );
66         var_dump( $oldLinks );
67         var_dump( $newLinks );
68         var_dump( $addedLinks );
69         die( '---' );
70         */
71         
72         if( $numLinks > 0 ) {
73                 wfDebug( "ConfirmEdit found $numLinks new links...\n" );
74                 if( ceKeyMatch() ) {
75                         wfDebug( "ConfirmEdit given proper key from form, passing.\n" );
76                         return true;
77                 } else {
78                         wfDebug( "ConfirmEdit missing form key, prompting.\n" );
79                         $editPage->showEditForm( 'ceFormCallback' );
80                         return false;
81                 }
82         } else {
83                 wfDebug( "ConfirmEdit: no new links.\n" );
84                 return true;
85         }
86 }
87
88 function ceKeyMatch() {
89         global $wgUser;
90         if( $wgUser->isAllowed( 'skipcaptcha' ) ) {
91                 wfDebug( "ConfirmEdit: user group allows skipping captcha\n" );
92                 return true;
93         }
94
95         global $wgEmailAuthentication, $ceAllowConfirmedEmail;
96         if( $wgEmailAuthentication && $ceAllowConfirmedEmail &&
97                 $wgUser->isEmailConfirmed() ) {
98                 wfDebug( "ConfirmEdit: user has confirmed mail, skippng captcha\n" );
99                 return true;
100         }
101         
102         if( !isset( $_SESSION['ceAnswerVar'] ) ) {
103                 wfDebug( "ConfirmEdit no session captcha key set, this is new visitor.\n" );
104                 return false;
105         }
106         global $wgRequest;
107         return $wgRequest->getVal( $_SESSION['ceAnswerVar'] ) == $_SESSION['ceAnswer'];
108 }
109
110 function ceFormCallback( &$out ) {
111         $source = 'ceSource' . mt_rand();
112         $dest = 'ceConfirm' . mt_rand();
113         
114         $a = mt_rand(0, 100);
115         $b = mt_rand(0, 10);
116         $op = mt_rand(0, 1) ? '+' : '-';
117         
118         $test = "$a $op $b";
119         $answer = ($op == '+') ? ($a + $b) : ($a - $b);
120         $_SESSION['ceAnswer'] = $answer;
121         $_SESSION['ceAnswerVar'] = $dest;
122         
123         
124         $out->addWikiText( wfMsg( "captcha-short" ) );  
125         $out->addHTML( <<<END
126                 <p><span id="$source">$test</span> = <input name="$dest" id="$dest" /></p>
127 END
128                 );
129 }
130
131 function ceLoadText( $editPage, $section ) {
132         $rev = Revision::newFromTitle( $editPage->mTitle );
133         if( is_null( $rev ) ) {
134                 return "";
135         } else {
136                 $text = $rev->getText();
137                 if( $section != '' ) {
138                         return Article::getSection( $text, $section );
139                 } else {
140                         return $text;
141                 }
142         }
143 }
144
145 function ceFindLinks( $text ) {
146         $regex = '/((?:' . HTTP_PROTOCOLS . ')' . EXT_LINK_URL_CLASS . '+)/';
147         
148         if( preg_match_all( $regex, $text, $matches, PREG_PATTERN_ORDER ) ) {
149                 return $matches[1];
150         } else {
151                 return array();
152         }
153 }
154
155 function ceShowHelp() {
156         global $wgOut, $ceAllowConfirmedEmail;
157         $wgOut->setPageTitle( 'Captcha help' );
158         $wgOut->addWikiText( <<<END
159 So what's this wacky captcha thing about?
160
161 It's your enemy. It's here to kill you. RUN WHILE YOU STILL CAN
162 END
163                 );
164 }
165
166 } # End invocation guard
167 ?>