Completed
Pull Request — master (#221)
by Maximilian
10:25 queued 07:13
created

functions.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**************************************************************************
3
 **********      English Wikipedia Account Request Interface      **********
4
 ***************************************************************************
5
 ** Wikipedia Account Request Graphic Design by Charles Melbye,           **
6
 ** which is licensed under a Creative Commons                            **
7
 ** Attribution-Noncommercial-Share Alike 3.0 United States License.      **
8
 **                                                                       **
9
 ** All other code are released under the Public Domain                   **
10
 ** by the ACC Development Team.                                          **
11
 **                                                                       **
12
 ** See CREDITS for the list of developers.                               **
13
 ***************************************************************************/
14
15
global $ACC;
16
global $baseurl;
17
global $dontUseWikiDb;
18
19
if (!defined("ACC")) {
20
	die();
21
} // Invalid entry point
22
23
require_once 'queryBrowser.php';
24
require_once 'includes/session.php';
25
26
// Initialize the class objects.
27
$session = new session();
28
29
/**
30
 * Send a "close pend ticket" email to the end user. (created, taken, etc...)
31
 */
32
function sendemail($messageno, $target, $id)
33
{
34
	$template = EmailTemplate::getById($messageno, gGetDb());
35
	$headers = 'From: [email protected]';
36
	    
37
	// Get the closing user's Email signature and append it to the Email.
38
	if (User::getCurrent()->getEmailSig() != "") {
39
		$emailsig = html_entity_decode(User::getCurrent()->getEmailSig(), ENT_QUOTES, "UTF-8");
40
		mail($target, "RE: [ACC #$id] English Wikipedia Account Request", $template->getText() . "\n\n" . $emailsig, $headers);
41
	}
42
	else {
43
		mail($target, "RE: [ACC #$id] English Wikipedia Account Request", $template->getText(), $headers);
44
	}
45
}
46
47
/**
48
 * Returns a value indicating whether the current request was issued over HTTPSs
49
 * @return bool true if HTTPS
50
 */
51
function isHttps() {
0 ignored issues
show
isHttps uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
52 View Code Duplication
	if (isset($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
53
		if($_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
0 ignored issues
show
The if-else statement can be simplified to return $_SERVER['HTTP_X_...ED_PROTO'] === 'https';.
Loading history...
54
			// Client <=> Proxy is encrypted
55
			return true;
56
		}
57
		else {
58
			// Proxy <=> Server link is encrypted, but not Client <=> Proxy.
59
			return false;
60
		}
61
	}
62
63 View Code Duplication
	if (isset($_SERVER['HTTPS'])) {
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
64
		if($_SERVER['HTTPS'] === 'off') {
65
			// ISAPI on IIS breaks the spec. :(
66
			return false;
67
		}
68
69
		if($_SERVER['HTTPS'] !== '') {
70
			// Set to a non-empty value
71
			return true;
72
		}
73
	}
74
75
	return false;
76
}
77
78
/**
79
 * Show the login page
80
 */
81
function showlogin()
82
{
83
	global $smarty;
84
    
85
	// Check whether there are any errors.
86
	$errorbartext = "";
87
	if (isset($_GET['error'])) {
88
		if ($_GET['error'] == 'authfail') {
89
			$errorbartext = BootstrapSkin::displayAlertBox("Username and/or password incorrect. Please try again.", "alert-error", "Auth failure", true, false, true);
90
		}
91
		elseif ($_GET['error'] == 'noid') {
92
			$errorbartext = BootstrapSkin::displayAlertBox("User account is not identified. Please email [email protected] if you believe this is in error.", "alert-error", "Auth failure", true, false, true);
93
		}
94
		elseif ($_GET['error'] == 'newacct') {
95
			$errorbartext = BootstrapSkin::displayAlertBox("I'm sorry, but, your account has not been approved by a site administrator yet. Please stand by.", "alert-info", "Account pending", true, false, true);
96
		}
97
	}
98
	$smarty->assign("errorbar", $errorbartext);   
99
100
	global $strictTransportSecurityExpiry;
101
	if ($strictTransportSecurityExpiry !== false) {
102
		if (isHttps()) {
103
			// Client can clearly use HTTPS, so let's enforce it for all connections.
104
			header("Strict-Transport-Security: max-age=15768000");
105
		}
106
		else {
107
			// This is the login form, not the request form. We need protection here.
108
			$path = 'https://' . $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"];
109
			header("Location: " . $path);
110
		}
111
	}
112
113
	$smarty->display("login.tpl");
114
}
115
116
function defaultpage()
117
{
118
	global $availableRequestStates, $defaultRequestStateKey, $requestLimitShowOnly, $enableEmailConfirm;
119
    
120
	$database = gGetDb();
121
    
122
	$requestSectionData = array();
123
    
124
	if ($enableEmailConfirm == 1) {
125
		$query = "SELECT * FROM request WHERE status = :type AND emailconfirm = 'Confirmed' LIMIT :lim;";
126
		$totalquery = "SELECT COUNT(*) FROM request WHERE status = :type AND emailconfirm = 'Confirmed';";
127
	}
128
	else {
129
		$query = "SELECT * FROM request WHERE status = :type LIMIT :lim;";
130
		$totalquery = "SELECT COUNT(*) FROM request WHERE status = :type;";
131
	}
132
    
133
	$statement = $database->prepare($query);
134
	$statement->bindValue(":lim", $requestLimitShowOnly, PDO::PARAM_INT);
135
    
136
	$totalRequestsStatement = $database->prepare($totalquery);
137
            
138
	// list requests in each section
139
	foreach ($availableRequestStates as $type => $v) {
140
		$statement->bindValue(":type", $type);
141
		$statement->execute();
142
        
143
		$requests = $statement->fetchAll(PDO::FETCH_CLASS, "Request");
144
		foreach ($requests as $req) {
145
			$req->setDatabase($database);   
146
		}
147
148
		$totalRequestsStatement->bindValue(":type", $type);
149
		$totalRequestsStatement->execute();
150
		$totalRequests = $totalRequestsStatement->fetchColumn();
151
		$totalRequestsStatement->closeCursor();
152
        
153
		$requestSectionData[$v['header']] = array(
154
			"requests" => $requests, 
155
			"total" => $totalRequests, 
156
			"api" => $v['api']);
157
	}
158
    
159
	global $smarty;
160
	$smarty->assign("requestLimitShowOnly", $requestLimitShowOnly);
161
	
162
	$query = <<<SQL
163
		SELECT request.id, request.name, request.checksum
164
		FROM request 
165
		JOIN log ON log.objectid = request.id and log.objecttype = 'Request'
166
		WHERE log.action LIKE 'Closed%' 
167
		ORDER BY log.timestamp DESC 
168
		LIMIT 5;
169
SQL;
170
    
171
	$statement = $database->prepare($query);
172
	$statement->execute();
173
    
174
	$last5result = $statement->fetchAll(PDO::FETCH_ASSOC);
175
    
176
	$smarty->assign("lastFive", $last5result);
177
	$smarty->assign("requestSectionData", $requestSectionData);
178
	$html = $smarty->fetch("mainpage/mainpage.tpl");
179
    
180
	return $html;
181
}
182
183
function array_search_recursive($needle, $haystack, $path = array())
184
{
185
	foreach ($haystack as $id => $val) {
186
		$path2 = $path;
187
		$path2[] = $id;
188
189
		if ($val === $needle) {
190
				return $path2;
191
		}
192
		else if (is_array($val)) {
193
				if ($ret = array_search_recursive($needle, $val, $path2)) {
194
						return $ret;
195
				}
196
		}
197
	}
198
	return false;
199
}
200
201
require_once('zoompage.php');
202
203
function displayPreview($wikicode)
204
{
205
	$parseresult = unserialize(file_get_contents('http://en.wikipedia.org/w/api.php?action=parse&format=php&text=' . urlencode($wikicode)));
206
	$out = "<br />\n<h3>Preview</h3>\n<div style=\"border: 2px dashed rgb(26, 79, 133);\">\n<div style=\"margin: 20px;\">";
207
	$out .= $parseresult['parse']['text']['*'];
208
	$out .= '</div></div>';
209
	return $out;
210
}
211
212
/**
213
 * Parses an XFF header and client IP to find the last trusted client IP
214
 * 
215
 * @param string $dbip The IP address the request came from
216
 * @param string $dbproxyip The contents of the XFF header of the request
217
 * @return string
218
 */
219
function getTrustedClientIP($dbip, $dbproxyip)
220
{
221
	global $xffTrustProvider;
222
    
223
	$clientIpAddr = $dbip;
224
	if ($dbproxyip) {
225
		$ipList = explode(",", $dbproxyip);
226
		$ipList[] = $clientIpAddr;
227
		$ipList = array_reverse($ipList);
228
		
229
		foreach ($ipList as $ipnumber => $ip) {
230
			if ($xffTrustProvider->isTrusted(trim($ip)) && $ipnumber < (count($ipList) - 1)) {
231
				continue;
232
			}
233
			
234
			$clientIpAddr = $ip;
235
			break;
236
		}
237
	}
238
	
239
	return $clientIpAddr;
240
}
241
242
/**
243
 * Explodes a CIDR range into an array of addresses
244
 * 
245
 * @param string $range A CIDR-format range
246
 * @return array An array containing every IP address in the range
247
 */
248
function explodeCidr($range)
249
{
250
	$ip_arr = explode('/', $range);
251
252
	if (!isset($ip_arr[1])) {
253
		return array($range);
254
	}
255
	
256
	$blow = ( 
257
		str_pad(decbin(ip2long($ip_arr[0])), 32, "0", STR_PAD_LEFT) &
258
		str_pad(str_pad("", $ip_arr[1], "1"), 32, "0") 
259
		);
260
	$bhigh = ($blow | str_pad(str_pad("", $ip_arr[1], "0"), 32, "1"));
261
262
	$list = array();
263
264
	$bindecBHigh = bindec($bhigh);
265
	for ($x = bindec($blow); $x <= $bindecBHigh; $x++) {
266
		$list[] = long2ip($x);
267
	}
268
269
	return $list;
270
}
271
272
/**
273
 * Takes an array( "low" => "high" ) values, and returns true if $needle is in at least one of them.
274
 * @param string $ip
275
 * @param array $haystack
276
 */
277
function ipInRange($haystack, $ip)
278
{
279
	$needle = ip2long($ip);
280
281
	foreach ($haystack as $low => $high) {
282
		if (ip2long($low) <= $needle && ip2long($high) >= $needle) {
283
			return true;
284
		}
285
	}
286
    
287
	return false;
288
}
289
290
/**
291
 * @return string
292
 */
293
function welcomerbotRenderSig($creator, $sig)
294
{
295
	$signature = html_entity_decode($sig) . ' ~~~~~';
296
	if (!preg_match("/((\[\[[ ]*(w:)?[ ]*(en:)?)|(\{\{subst:))[ ]*User[ ]*:[ ]*" . $creator . "[ ]*(\]\]|\||\}\}|\/)/i", $signature)) {
297
		$signature = "--[[User:$creator|$creator]] ([[User talk:$creator|talk]]) ~~~~~";
298
	}
299
	return $signature;
300
}
301
302
/**
303
 * Transforms a date string into a relative representation of the date ("2 weeks ago").
304
 * @param string $input A string representing a date
305
 * @return string
306
 * @example {$variable|relativedate} from Smarty
307
 */
308
function relativedate($input)
309
{
310
	$now = new DateTime();
311
	$then = new DateTime($input);
312
    
313
	$secs = $now->getTimestamp() - $then->getTimestamp();
314
    
315
	$second = 1;
316
	$minute = 60 * $second;
317
	$minuteCut = 60 * $second;
318
	$hour = 60 * $minute;
319
	$hourCut = 60 * $minute;
320
	$day = 24 * $hour;
321
	$dayCut = 48 * $hour;
322
	$week = 7 * $day;
323
	$weekCut = 14 * $day;
324
	$month = 30 * $day;
325
	$year = 365 * $day;
326
    
327
	$pluralise = true;
328
    
329
	if ($secs <= 10) {
330
		$output = "just now";
331
		$pluralise = false;
332
	}
333
	elseif ($secs > 10 && $secs < $minuteCut) {
334
		$output = round($secs / $second) . " second";
335
	}
336
	elseif ($secs >= $minuteCut && $secs < $hourCut) {
337
		$output = round($secs / $minute) . " minute";
338
	}
339
	elseif ($secs >= $hourCut && $secs < $dayCut) {
340
		$output = round($secs / $hour) . " hour";
341
	}
342
	elseif ($secs >= $dayCut && $secs < $weekCut) {
343
		$output = round($secs / $day) . " day";
344
	}
345 View Code Duplication
	elseif ($secs >= $weekCut && $secs < $month) {
346
		$output = round($secs / $week) . " week";
347
	}
348 View Code Duplication
	elseif ($secs >= $month && $secs < $year) {
349
		$output = round($secs / $month) . " month";
350
	}
351
	elseif ($secs >= $year && $secs < $year * 10) {
352
		$output = round($secs / $year) . " year";
353
	}
354
	else {
355
		$output = "a long time ago";
356
		$pluralise = false;
357
	}
358
    
359
	if ($pluralise) {
360
		$output = (substr($output, 0, 2) <> "1 ") ? $output . "s ago" : $output . " ago";
361
	}
362
363
	return $output;
364
}
365
366
/**
367
 * Summary of reattachOAuthAccount
368
 * @param User $user 
369
 * @throws TransactionException 
370
 */
371 View Code Duplication
function reattachOAuthAccount(User $user)
372
{
373
	global $oauthConsumerToken, $oauthSecretToken, $oauthBaseUrl, $oauthBaseUrlInternal;
374
375
	try {
376
		// Get a request token for OAuth
377
		$util = new OAuthUtility($oauthConsumerToken, $oauthSecretToken, $oauthBaseUrl, $oauthBaseUrlInternal);
378
		$requestToken = $util->getRequestToken();
379
380
		// save the request token for later
381
		$user->setOAuthRequestToken($requestToken->key);
382
		$user->setOAuthRequestSecret($requestToken->secret);
383
		$user->save();
384
            
385
		$redirectUrl = $util->getAuthoriseUrl($requestToken);
386
            
387
		header("Location: {$redirectUrl}");
388
		die();
389
	}
390
	catch (Exception $ex) {
391
		throw new TransactionException($ex->getMessage(), "Connection to Wikipedia failed.", "alert-error", 0, $ex);
392
	}     
393
}
394
395
/**
396
 * Generates the JavaScript source for XSS-safe typeahead autocompletion for usernames.  This output is expected to be
397
 * passed as the $tailscript argument to \BootstrapSkin::displayInternalFooter().
398
 *
399
 * @param $users string[] Array of usernames as strings
400
 * @return string
401
 */
402
function getTypeaheadSource($users) {
403
	$userList = "";
404
	foreach ($users as $v) {
405
		$userList .= "\"" . htmlentities($v) . "\", ";
406
	}
407
	$userList = "[" . rtrim($userList, ", ") . "]";
408
	$tailscript = <<<JS
409
$('.username-typeahead').typeahead({
410
	source: {$userList}
411
});
412
JS;
413
	return $tailscript;
414
}
415