This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /** |
||
3 | * RfA Analysis Library, modified for use with Peachy |
||
4 | * Copyright (C) 2006 Tangotango (tangotango.wp _at_ gmail _dot_ com) |
||
5 | * |
||
6 | * This program is free software; you can redistribute it and/or |
||
7 | * modify it under the terms of the GNU General Public License |
||
8 | * as published by the Free Software Foundation; either version 2 |
||
9 | * of the License, or (at your option) any later version. |
||
10 | * |
||
11 | * This program is distributed in the hope that it will be useful, |
||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
14 | * GNU General Public License for more details. |
||
15 | * |
||
16 | * You should have received a copy of the GNU General Public License |
||
17 | * along with this program; if not, write to the Free Software |
||
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
19 | */ |
||
20 | |||
21 | /** |
||
22 | * An RFA object contains the parsed information for an RFA |
||
23 | */ |
||
24 | class RFA { |
||
25 | |||
26 | protected $pgUsername = false; |
||
27 | protected $enddate = false; |
||
28 | protected $support = array(); |
||
29 | protected $oppose = array(); |
||
30 | protected $neutral = array(); |
||
31 | protected $duplicates = array(); |
||
32 | protected $lasterror = ''; |
||
33 | |||
34 | /** |
||
35 | * Analyzes an RFA. Returns TRUE on success, FALSE on failure |
||
36 | * @param Wiki $wiki |
||
37 | * @param $page |
||
38 | * @param null $rawwikitext |
||
39 | */ |
||
40 | public function __construct( Wiki $wiki, $page, $rawwikitext = null ) { |
||
41 | if( is_null( $rawwikitext ) ) $rawwikitext = $wiki->initPage( $page )->get_text(); |
||
42 | |||
43 | $split = preg_split( |
||
44 | "/^(?:(?:'''|(?:<includeonly><noin<\/includeonly><includeonly>clude><\/includeonly>)?={4,5}(?:<includeonly><\/noin<\/includeonly><includeonly>clude><\/includeonly>''')?)" |
||
45 | . "\s*?(Support|Oppose|Neutral|Comments)\s*?(?:'''|(?:'''<includeonly><noin<\/includeonly><includeonly>clude><\/includeonly>)?={4,5}(?:<includeonly><\/noin<\/includeonly><includeonly>clude><\/includeonly>)?)|;\s*(Support|Oppose|Neutral|Comments))\s*(?:<br>|<br \/>)?\s*$/im" |
||
46 | , $rawwikitext, -1, PREG_SPLIT_DELIM_CAPTURE |
||
47 | ); |
||
48 | |||
49 | $header = array_shift( $split ); |
||
50 | |||
51 | //=== Deal with the header ===// |
||
52 | $header = str_ireplace( array( '<nowiki>', '</nowiki>' ), '', $header ); |
||
53 | |||
54 | if( preg_match( "/===\s*\[\[User:(.*?)\|.*?\]\]\s*===/", $header, $matches ) ) { |
||
55 | $this->username = $matches[1]; |
||
56 | } elseif( preg_match( "/===\s*\[\[.*?\|(.*?)\]\]\s*===/", $header, $matches ) ) { |
||
57 | $this->username = $matches[1]; |
||
58 | } |
||
59 | |||
60 | $header = str_replace( array( '[[', ']]' ), '', $header ); |
||
61 | |||
62 | if( preg_match( "/end(?:ing|ed)?(?: no earlier than)? (.*?) \(UTC\)/i", $header, $matches ) ) { |
||
63 | $this->enddate = $matches[1]; |
||
64 | } |
||
65 | //=== End header stuff ===// |
||
66 | |||
67 | //Now parse through each non-header section, figuring out what they are |
||
68 | //Nothing expected = 0, Support = 1, Oppose = 2, Neutral = 3 |
||
69 | $nextsection = 0; |
||
70 | |||
71 | foreach( $split as $splut ){ |
||
72 | $splut = trim( $splut ); |
||
73 | if( empty( $splut ) ) { |
||
74 | continue; |
||
75 | } |
||
76 | |||
77 | if( strcasecmp( $splut, 'Support' ) == 0 ) { |
||
78 | $nextsection = 1; |
||
79 | } elseif( strcasecmp( $splut, 'Oppose' ) == 0 ) { |
||
80 | $nextsection = 2; |
||
81 | } elseif( strcasecmp( $splut, 'Neutral' ) == 0 ) { |
||
82 | $nextsection = 3; |
||
83 | } else { |
||
84 | switch( $nextsection ){ |
||
85 | case 1: |
||
86 | $support = $splut; |
||
87 | break; |
||
88 | case 2: |
||
89 | $oppose = $splut; |
||
90 | break; |
||
91 | case 3: |
||
92 | $neutral = $splut; |
||
93 | break; |
||
94 | } |
||
95 | $nextsection = 0; |
||
96 | } |
||
97 | } |
||
98 | |||
99 | if( !isset( $support ) ) { |
||
100 | $this->lasterror = "Support section not found"; |
||
101 | return false; |
||
102 | } |
||
103 | if( !isset( $oppose ) ) { |
||
104 | $this->lasterror = "Oppose section not found"; |
||
105 | return false; |
||
106 | } |
||
107 | if( !isset( $neutral ) ) { |
||
108 | $this->lasterror = "Neutral section not found"; |
||
109 | return false; |
||
110 | } |
||
111 | |||
112 | $this->support = $this->analyzeSection( $support ); |
||
113 | $this->oppose = $this->analyzeSection( $oppose ); |
||
114 | $this->neutral = $this->analyzeSection( $neutral ); |
||
115 | |||
116 | //Merge all votes in one array and sort: |
||
117 | $m = array(); |
||
118 | foreach( $this->support as $s ){ |
||
119 | if( isset( $s['name'] ) ) $m[] = $s['name']; |
||
120 | } |
||
121 | foreach( $this->oppose as $o ){ |
||
122 | if( isset( $o['name'] ) ) $m[] = $o['name']; |
||
123 | } |
||
124 | foreach( $this->neutral as $n ){ |
||
125 | if( isset( $n['name'] ) ) $m[] = $n['name']; |
||
126 | } |
||
127 | sort( $m ); |
||
128 | //Find duplicates: |
||
129 | for( $i = 0; $i < count( $m ); $i++ ){ |
||
130 | if( $i != count( $m ) - 1 ) { |
||
131 | if( $m[$i] == $m[$i + 1] ) { |
||
132 | $this->duplicates[] = $m[$i]; |
||
133 | } |
||
134 | } |
||
135 | } |
||
136 | |||
137 | return true; |
||
138 | } |
||
139 | |||
140 | public function get_username() { |
||
141 | return $this->username; |
||
142 | } |
||
143 | |||
144 | public function get_enddate() { |
||
145 | return $this->enddate; |
||
146 | } |
||
147 | |||
148 | public function get_support() { |
||
149 | return $this->support; |
||
150 | } |
||
151 | |||
152 | public function get_oppose() { |
||
153 | return $this->oppose; |
||
154 | } |
||
155 | |||
156 | public function get_neutral() { |
||
157 | return $this->neutral; |
||
158 | } |
||
159 | |||
160 | public function get_duplicates() { |
||
161 | return $this->duplicates; |
||
162 | } |
||
163 | |||
164 | public function get_lasterror() { |
||
165 | return $this->lasterror; |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * Attempts to find a signature in $input using the default regex. Returns matches. |
||
170 | * @param $input |
||
171 | * @param $matches |
||
172 | * |
||
173 | * @return int |
||
174 | */ |
||
175 | protected function findSig( $input, &$matches ) { |
||
176 | //Supports User: and User talk: wikilinks, {{fullurl}}, unsubsted {{unsigned}}, unsubsted {{unsigned2}}, anything that looks like a custom sig template |
||
177 | return preg_match_all( |
||
178 | "/\[\[[Uu]ser(?:[\s_][Tt]alk)?\:([^\]\|\/]*)(?:\|[^\]]*)?\]\]" //1: Normal [[User:XX]] and [[User talk:XX]] |
||
179 | . "|\{\{(?:[Ff]ullurl\:[Uu]ser(?:[\s_][Tt]alk)?\:|[Uu]nsigned\|)([^\}\|]*)(?:|[\|\}]*)?\}\}" //2: {{fullurl}} and {{unsigned}} templates |
||
180 | . "|(?:\{\{)[Uu]ser(?:[\s_][Tt]alk)?\:([^\}\/\|]*)" //3: {{User:XX/sig}} templates |
||
181 | . "|\{\{[Uu]nsigned2\|[^\|]*\|([^\}]*)\}\}" //4: {{unsigned2|Date|XX}} templates |
||
182 | . "|(?:\[\[)[Uu]ser\:([^\]\/\|]*)\/[Ss]ig[\|\]]/" //5: [[User:XX/sig]] links (compromise measure) |
||
183 | , $input, $matches, PREG_OFFSET_CAPTURE |
||
184 | ); |
||
185 | } |
||
186 | |||
187 | /** |
||
188 | * Attempts to find a signature in $input using a different regex. Returns matches. |
||
189 | * @param $input |
||
190 | * @param $matches |
||
191 | * |
||
192 | * @return int |
||
193 | */ |
||
194 | protected function findSigAlt( $input, &$matches ) { |
||
195 | return preg_match_all( |
||
196 | "/\[\[[Uu]ser(?:[\s_][Tt]alk)?\:([^\]\/\|]*)" //5: "[[User:XX/PageAboutMe" links (notice no end tag) |
||
197 | . "|\[\[[Ss]pecial\:[Cc]ontributions\/([^\|\]]*)/" |
||
198 | , $input, $matches, PREG_OFFSET_CAPTURE |
||
199 | ); |
||
200 | } |
||
201 | |||
202 | /** |
||
203 | * Attempts to find a signature in $input. Returns the name of the user, false on failure. |
||
204 | * @param $input |
||
205 | * @param $iffy |
||
206 | * |
||
207 | * @return bool|string false if not found Signature, or the Signature if it is found |
||
208 | */ |
||
209 | protected function findSigInLine( $input, &$iffy ) { |
||
210 | $iffy = 0; |
||
211 | |||
212 | $parsee_array = explode( "\n", $input ); |
||
213 | for( $n = 0; $n < count( $parsee_array ); $n++ ){ //This for will terminate when a sig is found. |
||
0 ignored issues
–
show
Consider avoiding function calls on each iteration of the
for loop.
If you have a function call in the test part of a // count() is called on each iteration
for ($i=0; $i < count($collection); $i++) { }
// count() is only called once
for ($i=0, $c=count($collection); $i<$c; $i++) { }
![]() |
|||
214 | $parsee = $parsee_array[$n]; |
||
215 | //Okay, let's try and remove "copied from above" messages. If the line has more than one timestamp, we'll disregard anything after the first. |
||
216 | //Note: we're ignoring people who use custom timestamps - if these peoples' votes are moved, the mover's name will show up as having voted. |
||
217 | |||
218 | //If more than one timestamp is found in the first portion of the vote: |
||
219 | $tsmatches = array(); |
||
220 | $dummymatches = array(); |
||
221 | if( preg_match_all( '/' . "[0-2][0-9]\:[0-5][0-9], [1-3]?[0-9] (?:January|February|March|April|May|June|July|August|September|October|November|December) \d{4} \(UTC\)" . '/', $parsee, $tsmatches, PREG_OFFSET_CAPTURE ) > 1 ) { |
||
222 | //Go through each timestamp-section, looking for a signature |
||
223 | foreach( $tsmatches[0] as $minisection ){ |
||
224 | $temp = substr( $parsee, 0, $minisection[1] ); |
||
225 | //If a signature is found, stop and use it as voter |
||
226 | if( $this->findSig( $temp, $dummymatches ) != 0 ) { //KNOWN ISSUE: Write description later |
||
227 | $parsee = $temp; |
||
228 | break; |
||
229 | } |
||
230 | } |
||
231 | } |
||
232 | |||
233 | //Start the main signature-finding: |
||
234 | $matches = array(); |
||
235 | if( $this->findSig( $parsee, $matches ) == 0 ) { |
||
236 | //Okay, signature not found. Let's try the backup regex |
||
237 | if( $this->findSigAlt( $parsee, $matches ) == 0 ) { |
||
238 | //Signature was not found in this iteration of the main loop :( |
||
239 | continue; //Go on to next newline (may be iffy) |
||
240 | } else { |
||
241 | $merged = array_merge( $matches[1], $matches[2] ); |
||
242 | } |
||
243 | } else { |
||
244 | //Merge the match arrays: |
||
245 | $merged = array_merge( $matches[5], $matches[1], $matches[3], $matches[2], $matches[4] ); |
||
246 | } |
||
247 | //Remove blank values and arrays of the form ('',-1): |
||
248 | foreach( $merged as $key => $value ){ |
||
249 | if( is_array( $value ) && ( $value[0] == '' ) && ( $value[1] == -1 ) ) { |
||
250 | unset( $merged[$key] ); |
||
251 | } elseif( $value == "" ) { |
||
252 | unset( $merged[$key] ); |
||
253 | } |
||
254 | } |
||
255 | |||
256 | //Let's find out the real signature |
||
257 | $keys = array(); |
||
258 | $values = array(); |
||
259 | foreach( $merged as $mergee ){ |
||
260 | $keys[] = $mergee[0]; |
||
261 | $values[] = $mergee[1]; |
||
262 | } |
||
263 | //Now sort: |
||
264 | array_multisort( $values, SORT_DESC, SORT_NUMERIC, $keys ); |
||
265 | //Now we should have the most relevant match (i.e., the sig) at the top of $keys |
||
266 | $i = 0; |
||
267 | $foundsig = ''; |
||
268 | while( $foundsig == '' ){ |
||
269 | $foundsig = trim( $keys[$i++] ); |
||
270 | if( $i == count( $keys ) ) break; //If we can only find blank usernames in the sig, catch overflow |
||
271 | //Also fires when the first sig is also the last sig, so not an error |
||
272 | } |
||
273 | |||
274 | //Set iffy flag (level 1) if went beyond first line |
||
275 | if( $n > 0 ) { |
||
276 | $iffy = 1; |
||
277 | } |
||
278 | return $foundsig; |
||
279 | } |
||
280 | |||
281 | return false; |
||
282 | } |
||
283 | |||
284 | /** |
||
285 | * Analyzes an RFA section. Returns an array of parsed signatures on success. Undefined behaviour on failure. |
||
286 | * @param string $input |
||
287 | * |
||
288 | * @return array |
||
289 | */ |
||
290 | private function analyzeSection( $input ) { |
||
291 | //Remove trailing sharp, if any |
||
292 | $input = preg_replace( '/#\s*$/', '', $input ); |
||
293 | |||
294 | //Old preg_split regex: "/(^|\n)\s*\#[^\#\:\*]/" |
||
295 | $parsed = preg_split( "/(^|\n)\#/", $input ); |
||
296 | //Shift off first empty element: |
||
297 | array_shift( $parsed ); |
||
298 | |||
299 | foreach( $parsed as &$parsee ){ //Foreach line |
||
300 | //If the line is empty for some reason, ignore |
||
301 | $parsee = trim( $parsee ); |
||
302 | if( empty( $parsee ) ) continue; |
||
303 | |||
304 | //If the line has been indented (disabled), or is a comment, ignore |
||
305 | if( ( $parsee[0] == ':' ) || ( $parsee[0] == '*' ) || ( $parsee[0] == '#' ) ) { |
||
306 | $parsee = '///###///'; |
||
307 | continue; |
||
308 | }; //struck-out vote or comment |
||
309 | |||
310 | $parsedsig = $this->findSigInLine( $parsee, $iffy ); //Find signature |
||
311 | $orgsig = $parsee; |
||
312 | $parsee = array(); |
||
313 | $parsee['context'] = $orgsig; |
||
314 | if( $parsedsig === false ) { |
||
315 | $parsee['error'] = 'Signature not found'; |
||
316 | } else { |
||
317 | $parsee['name'] = $parsedsig; |
||
318 | } |
||
319 | if( @$iffy == 1 ) { |
||
320 | $parsee['iffy'] = '1'; |
||
321 | } |
||
322 | } //Foreach line |
||
323 | |||
324 | if( ( count( $parsed ) == 1 ) && ( @trim( $parsed[0]['name'] ) == '' ) ) { //filters out placeholder sharp sign used in empty sections |
||
325 | $parsed = array(); |
||
326 | } |
||
327 | |||
328 | //Delete struck-out keys "continued" in foreach |
||
329 | foreach( $parsed as $key => $value ){ |
||
330 | if( $value == '///###///' ) { |
||
331 | unset( $parsed[$key] ); |
||
332 | } |
||
333 | } |
||
334 | |||
335 | return $parsed; |
||
336 | } |
||
337 | |||
338 | } |
||
339 |
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: