aur.inc.php 18.8 KB
Newer Older
1
<?php
2
set_include_path(get_include_path() . PATH_SEPARATOR . '../lib' . PATH_SEPARATOR . '../template');
pjmattal's avatar
pjmattal committed
3
header('Content-Type: text/html; charset=utf-8');
simo's avatar
simo committed
4
5
6
header('Cache-Control: no-cache, must-revalidate');
header('Expires: Tue, 11 Oct 1988 22:00:00 GMT'); // quite a special day
header('Pragma: no-cache');
7
header('X-Frame-Options: DENY');
8

Dan McGee's avatar
Dan McGee committed
9
10
date_default_timezone_set('UTC');

11
include_once('translator.inc.php');
12
13
set_lang();

14
include_once("DB.class.php");
15
include_once("routing.inc.php");
16
17
include_once("version.inc.php");
include_once("acctfuncs.inc.php");
18
include_once("cachefuncs.inc.php");
19
include_once("confparser.inc.php");
Lukas Fleischer's avatar
Lukas Fleischer committed
20
include_once("credentials.inc.php");
elij's avatar
elij committed
21

Mark Weiman's avatar
Mark Weiman committed
22
23
24
include_once('timezone.inc.php');
set_tz();

25
check_sid();
26
check_tos();
27

28
29
30
31
32
33
34
35
36
37
38
/**
 * Check if a visitor is logged in
 *
 * Query "Sessions" table with supplied cookie. Determine if the cookie is valid
 * or not. Unset the cookie if invalid or session timeout reached. Update the
 * session timeout if it is still valid.
 *
 * @global array $_COOKIE User cookie values
 *
 * @return void
 */
39
function check_sid() {
eric's avatar
eric committed
40
41
	global $_COOKIE;

Loui Chang's avatar
Loui Chang committed
42
	if (isset($_COOKIE["AURSID"])) {
eric's avatar
eric committed
43
		$failed = 0;
44
		$timeout = config_get_int('options', 'login_timeout');
eric's avatar
eric committed
45
46
		# the visitor is logged in, try and update the session
		#
47
		$dbh = DB::connect();
48
		$q = "SELECT LastUpdateTS, " . strval(time()) . " FROM Sessions ";
canyonknight's avatar
canyonknight committed
49
50
51
52
		$q.= "WHERE SessionID = " . $dbh->quote($_COOKIE["AURSID"]);
		$result = $dbh->query($q);
		$row = $result->fetch(PDO::FETCH_NUM);

Lukas Fleischer's avatar
Lukas Fleischer committed
53
		if (!$row) {
eric's avatar
eric committed
54
55
			# Invalid SessionID - hacker alert!
			#
eric's avatar
eric committed
56
57
			$failed = 1;
		} else {
58
			$last_update = $row[0];
59
			if ($last_update + $timeout <= $row[1]) {
eric's avatar
eric committed
60
				$failed = 2;
eric's avatar
eric committed
61
62
			}
		}
63

eric's avatar
eric committed
64
65
		if ($failed == 1) {
			# clear out the hacker's cookie, and send them to a naughty page
66
			# why do you have to be so harsh on these people!?
eric's avatar
eric committed
67
			#
68
			setcookie("AURSID", "", 1, "/", null, !empty($_SERVER['HTTPS']), true);
69
			unset($_COOKIE['AURSID']);
eric's avatar
eric committed
70
		} elseif ($failed == 2) {
Dan McGee's avatar
Dan McGee committed
71
			# session id timeout was reached and they must login again.
eric's avatar
eric committed
72
			#
73
			delete_session_id($_COOKIE["AURSID"]);
eric's avatar
eric committed
74

75
			setcookie("AURSID", "", 1, "/", null, !empty($_SERVER['HTTPS']), true);
76
			unset($_COOKIE['AURSID']);
eric's avatar
eric committed
77
78
79
		} else {
			# still logged in and haven't reached the timeout, go ahead
			# and update the idle timestamp
80
81

			# Only update the timestamp if it is less than the
82
			# current time plus $timeout.
eric's avatar
eric committed
83
			#
84
85
			# This keeps 'remembered' sessions from being
			# overwritten.
86
			if ($last_update < time() + $timeout) {
87
				$q = "UPDATE Sessions SET LastUpdateTS = " . strval(time()) . " ";
canyonknight's avatar
canyonknight committed
88
89
				$q.= "WHERE SessionID = " . $dbh->quote($_COOKIE["AURSID"]);
				$dbh->exec($q);
90
			}
eric's avatar
eric committed
91
92
93
94
95
		}
	}
	return;
}

96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/**
 * Redirect user to the Terms of Service agreement if there are updated terms.
 *
 * @return void
 */
function check_tos() {
	if (!isset($_COOKIE["AURSID"])) {
		return;
	}

	$path = $_SERVER['PATH_INFO'];
	$route = get_route($path);
	if (!$route || $route == "tos.php") {
		return;
	}

	if (count(fetch_updated_terms(uid_from_sid($_COOKIE["AURSID"]))) > 0) {
		header('Location: ' . get_uri('/tos'));
		exit();
	}
}

118
119
120
121
122
/**
 * Verify the supplied CSRF token matches expected token
 *
 * @return bool True if the CSRF token is the same as the cookie SID, otherwise false
 */
123
function check_token() {
124
	if (isset($_POST['token']) && isset($_COOKIE['AURSID'])) {
125
126
127
128
129
130
		return ($_POST['token'] == $_COOKIE['AURSID']);
	} else {
		return false;
	}
}

131
132
133
134
135
136
137
/**
 * Verify a user supplied e-mail against RFC 3696 and DNS records
 *
 * @param string $addy E-mail address being validated in foo@example.com format
 *
 * @return bool True if e-mail passes validity checks, otherwise false
 */
eric's avatar
eric committed
138
function valid_email($addy) {
139
140
141
142
143
144
145
146
147
148
149
150
	// check against RFC 3696
	if (filter_var($addy, FILTER_VALIDATE_EMAIL) === false) {
		return false;
	}

	// check dns for mx, a, aaaa records
	list($local, $domain) = explode('@', $addy);
	if (!(checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A') || checkdnsrr($domain, 'AAAA'))) {
		return false;
	}

	return true;
eric's avatar
eric committed
151
152
}

153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/**
 * Verify that a given URL is valid and uses the HTTP(s) protocol
 *
 * @param string $url URL of the home page to be validated
 *
 * @return bool True if URL passes validity checks, false otherwise
 */
function valid_homepage($url) {
	if (filter_var($url, FILTER_VALIDATE_URL) === false) {
		return false;
	}

	$url_components = parse_url($url);
	if (!in_array($url_components['scheme'], array('http', 'https'))) {
		return false;
	}

	return true;
}

173
174
175
176
177
/**
 * Generate a unique session ID
 *
 * @return string MD5 hash of the concatenated user IP, random number, and current time
 */
eric's avatar
eric committed
178
function new_sid() {
179
	return md5($_SERVER['REMOTE_ADDR'] . uniqid(mt_rand(), true));
eric's avatar
eric committed
180
181
}

182
183
184
185
186
/**
 * Determine the user's username in the database using a user ID
 *
 * @param string $id User's ID
 *
187
 * @return string Username if it exists, otherwise null
188
 */
189
190
191
function username_from_id($id) {
	$id = intval($id);

192
	$dbh = DB::connect();
canyonknight's avatar
canyonknight committed
193
194
	$q = "SELECT Username FROM Users WHERE ID = " . $dbh->quote($id);
	$result = $dbh->query($q);
195
	if (!$result) {
196
		return null;
197
198
	}

199
	$row = $result->fetch(PDO::FETCH_NUM);
Eli Schwartz's avatar
Eli Schwartz committed
200
201
202
	if ($row) {
		return $row[0];
	}
203
204
}

205
206
207
208
209
210
211
/**
 * Determine the user's username in the database using a session ID
 *
 * @param string $sid User's session ID
 *
 * @return string Username of the visitor
 */
212
function username_from_sid($sid="") {
eric's avatar
eric committed
213
214
215
	if (!$sid) {
		return "";
	}
216
	$dbh = DB::connect();
eric's avatar
eric committed
217
218
219
	$q = "SELECT Username ";
	$q.= "FROM Users, Sessions ";
	$q.= "WHERE Users.ID = Sessions.UsersID ";
canyonknight's avatar
canyonknight committed
220
221
	$q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
	$result = $dbh->query($q);
eric's avatar
eric committed
222
223
224
	if (!$result) {
		return "";
	}
canyonknight's avatar
canyonknight committed
225
	$row = $result->fetch(PDO::FETCH_NUM);
eric's avatar
eric committed
226

Eli Schwartz's avatar
Eli Schwartz committed
227
228
229
	if ($row) {
		return $row[0];
	}
eric's avatar
eric committed
230
231
}

232
233
234
235
236
/**
 * Format a user name for inclusion in HTML data
 *
 * @param string $username The user name to format
 *
237
 * @return string The generated HTML code for the account link
238
239
 */
function html_format_username($username) {
240
241
	$username_fmt = $username ? htmlspecialchars($username, ENT_QUOTES) : __("None");

242
	if ($username && isset($_COOKIE["AURSID"])) {
243
244
245
246
247
248
249
		$link = '<a href="' . get_uri('/account/') . $username_fmt;
		$link .= '" title="' . __('View account information for %s', $username_fmt);
		$link .= '">' . $username_fmt . '</a>';
		return $link;
	} else {
		return $username_fmt;
	}
250
251
}

252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
/**
 * Format the maintainer and co-maintainers for inclusion in HTML data
 *
 * @param string $maintainer The user name of the maintainer
 * @param array $comaintainers The list of co-maintainer user names
 *
 * @return string The generated HTML code for the account links
 */
function html_format_maintainers($maintainer, $comaintainers) {
	$code = html_format_username($maintainer);

	if (count($comaintainers) > 0) {
		$code .= ' (';
		foreach ($comaintainers as $comaintainer) {
			$code .= html_format_username($comaintainer);
			if ($comaintainer !== end($comaintainers)) {
				$code .= ', ';
			}
		}
		$code .= ')';
	}

	return $code;
}

277
278
279
280
/**
 * Format a link in the package actions box
 *
 * @param string $uri The link target
281
 * @param string $inner The HTML code to use for the link label
282
283
284
 *
 * @return string The generated HTML code for the action link
 */
285
function html_action_link($uri, $inner) {
286
287
288
289
290
291
	if (isset($_COOKIE["AURSID"])) {
		$code = '<a href="' . htmlspecialchars($uri, ENT_QUOTES) . '">';
	} else {
		$code = '<a href="' . get_uri('/login/', true) . '?referer=';
		$code .= urlencode(rtrim(aur_location(), '/') . $uri) . '">';
	}
292
	$code .= $inner . '</a>';
293
294
295
296
297
298
299
300
301

	return $code;
}

/**
 * Format a form in the package actions box
 *
 * @param string $uri The link target
 * @param string $action The action name (passed as HTTP POST parameter)
302
 * @param string $inner The HTML code to use for the link label
303
304
305
 *
 * @return string The generated HTML code for the action link
 */
306
function html_action_form($uri, $action, $inner) {
307
308
309
310
311
312
313
	if (isset($_COOKIE["AURSID"])) {
		$code = '<form action="' . htmlspecialchars($uri, ENT_QUOTES) . '" ';
		$code .= 'method="post">';
		$code .= '<input type="hidden" name="token" value="';
		$code .= htmlspecialchars($_COOKIE['AURSID'], ENT_QUOTES) . '" />';
		$code .= '<input type="submit" class="button text-button" name="';
		$code .= htmlspecialchars($action, ENT_QUOTES) . '" ';
314
		$code .= 'value="' . $inner . '" />';
315
316
317
		$code .= '</form>';
	} else {
		$code = '<a href="' . get_uri('/login/', true) . '">';
318
		$code .= $inner . '</a>';
319
	}
320
321
322
323

	return $code;
}

324
325
326
327
328
329
330
/**
 * Determine the user's e-mail address in the database using a session ID
 *
 * @param string $sid User's session ID
 *
 * @return string User's e-mail address as given during registration
 */
331
function email_from_sid($sid="") {
eric's avatar
eric committed
332
333
334
	if (!$sid) {
		return "";
	}
335
	$dbh = DB::connect();
eric's avatar
eric committed
336
337
338
	$q = "SELECT Email ";
	$q.= "FROM Users, Sessions ";
	$q.= "WHERE Users.ID = Sessions.UsersID ";
canyonknight's avatar
canyonknight committed
339
340
	$q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
	$result = $dbh->query($q);
eric's avatar
eric committed
341
342
343
	if (!$result) {
		return "";
	}
canyonknight's avatar
canyonknight committed
344
	$row = $result->fetch(PDO::FETCH_NUM);
eric's avatar
eric committed
345

Eli Schwartz's avatar
Eli Schwartz committed
346
347
348
	if ($row) {
		return $row[0];
	}
eric's avatar
eric committed
349
350
}

351
352
353
354
355
356
357
/**
 * Determine the user's account type in the database using a session ID
 *
 * @param string $sid User's session ID
 *
 * @return string Account type of user ("User", "Trusted User", or "Developer")
 */
358
function account_from_sid($sid="") {
eric's avatar
eric committed
359
360
361
	if (!$sid) {
		return "";
	}
362
	$dbh = DB::connect();
eric's avatar
eric committed
363
364
365
	$q = "SELECT AccountType ";
	$q.= "FROM Users, AccountTypes, Sessions ";
	$q.= "WHERE Users.ID = Sessions.UsersID ";
366
	$q.= "AND AccountTypes.ID = Users.AccountTypeID ";
canyonknight's avatar
canyonknight committed
367
368
	$q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
	$result = $dbh->query($q);
eric's avatar
eric committed
369
370
371
	if (!$result) {
		return "";
	}
canyonknight's avatar
canyonknight committed
372
	$row = $result->fetch(PDO::FETCH_NUM);
eric's avatar
eric committed
373

Eli Schwartz's avatar
Eli Schwartz committed
374
375
376
	if ($row) {
		return $row[0];
	}
eric's avatar
eric committed
377
}
378

379
380
381
382
383
384
385
/**
 * Determine the user's ID in the database using a session ID
 *
 * @param string $sid User's session ID
 *
 * @return string|int The user's name, 0 on query failure
 */
386
function uid_from_sid($sid="") {
387
388
389
	if (!$sid) {
		return "";
	}
390
	$dbh = DB::connect();
391
392
393
	$q = "SELECT Users.ID ";
	$q.= "FROM Users, Sessions ";
	$q.= "WHERE Users.ID = Sessions.UsersID ";
canyonknight's avatar
canyonknight committed
394
395
	$q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
	$result = $dbh->query($q);
396
397
398
	if (!$result) {
		return 0;
	}
canyonknight's avatar
canyonknight committed
399
	$row = $result->fetch(PDO::FETCH_NUM);
400

Eli Schwartz's avatar
Eli Schwartz committed
401
402
403
	if ($row) {
		return $row[0];
	}
404
405
}

406
407
408
409
410
411
412
413
414
/**
 * Common AUR header displayed on all pages
 *
 * @global string $LANG Language selected by the visitor
 * @global array $SUPPORTED_LANGS Languages that are supported by the AUR
 * @param string $title Name of the AUR page to be displayed on browser
 *
 * @return void
 */
415
function html_header($title="", $details=array()) {
416
	global $LANG;
417
	global $SUPPORTED_LANGS;
418

419
420
	include('header.php');
	return;
421
422
}

423
424
425
426
427
428
429
/**
 * Common AUR footer displayed on all pages
 *
 * @param string $ver The AUR version
 *
 * @return void
 */
430
function html_footer($ver="") {
431
	include('footer.php');
432
433
434
	return;
}

435
436
437
438
439
440
441
442
/**
 * Determine if a user has permission to submit a package
 *
 * @param string $name Name of the package to be submitted
 * @param string $sid User's session ID
 *
 * @return int 0 if the user can't submit, 1 if the user can submit
 */
443
function can_submit_pkgbase($name="", $sid="") {
eric's avatar
eric committed
444
	if (!$name || !$sid) {return 0;}
445
	$dbh = DB::connect();
Dan McGee's avatar
Dan McGee committed
446
	$q = "SELECT MaintainerUID ";
447
	$q.= "FROM PackageBases WHERE Name = " . $dbh->quote($name);
canyonknight's avatar
canyonknight committed
448
449
450
451
452
453
	$result = $dbh->query($q);
	$row = $result->fetch(PDO::FETCH_NUM);

	if (!$row[0]) {
		return 1;
	}
454
	$my_uid = uid_from_sid($sid);
eric's avatar
eric committed
455

456
	if ($row[0] === NULL || $row[0] == $my_uid) {
457
458
		return 1;
	}
eric's avatar
eric committed
459
460
461
462

	return 0;
}

463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
/**
 * Determine if a package can be overwritten by some package base
 *
 * @param string $name Name of the package to be submitted
 * @param int $base_id The ID of the package base
 *
 * @return bool True if the package can be overwritten, false if not
 */
function can_submit_pkg($name, $base_id) {
	$dbh = DB::connect();
	$q = "SELECT COUNT(*) FROM Packages WHERE ";
	$q.= "Name = " . $dbh->quote($name) . " AND ";
	$q.= "PackageBaseID <> " . intval($base_id);
	$result = $dbh->query($q);

	if (!$result) return false;
	return ($result->fetchColumn() == 0);
}

482
483
484
485
486
487
488
/**
 * Recursively delete a directory
 *
 * @param string $dirname Name of the directory to be removed
 *
 * @return void
 */
489
490
491
492
493
494
495
496
497
498
499
500
501
function rm_tree($dirname) {
	if (empty($dirname) || !is_dir($dirname)) return;

	foreach (scandir($dirname) as $item) {
		if ($item != '.' && $item != '..') {
			$path = $dirname . '/' . $item;
			if (is_file($path) || is_link($path)) {
				unlink($path);
			}
			else {
				rm_tree($path);
			}
		}
eric's avatar
eric committed
502
	}
Loui Chang's avatar
Loui Chang committed
503

504
505
	rmdir($dirname);

eric's avatar
eric committed
506
507
508
	return;
}

509
510
511
512
513
 /**
 * Determine the user's ID in the database using a username
 *
 * @param string $username The username of an account
 *
514
 * @return string Return user ID if exists for username, otherwise null
515
 */
516
function uid_from_username($username) {
517
	$dbh = DB::connect();
canyonknight's avatar
canyonknight committed
518
519
	$q = "SELECT ID FROM Users WHERE Username = " . $dbh->quote($username);
	$result = $dbh->query($q);
simo's avatar
simo committed
520
	if (!$result) {
521
		return null;
simo's avatar
simo committed
522
	}
523

524
	$row = $result->fetch(PDO::FETCH_NUM);
Eli Schwartz's avatar
Eli Schwartz committed
525
526
527
	if ($row) {
		return $row[0];
	}
528
529
}

530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
/**
 * Determine the user's ID in the database using a username or email address
 *
 * @param string $username The username or email address of an account
 *
 * @return string Return user ID if exists, otherwise null
 */
function uid_from_loginname($loginname) {
	$uid = uid_from_username($loginname);
	if (!$uid) {
		$uid = uid_from_email($loginname);
	}
	return $uid;
}

545
546
547
548
549
550
551
/**
 * Determine the user's ID in the database using an e-mail address
 *
 * @param string $email An e-mail address in foo@example.com format
 *
 * @return string The user's ID
 */
552
function uid_from_email($email) {
553
	$dbh = DB::connect();
canyonknight's avatar
canyonknight committed
554
555
	$q = "SELECT ID FROM Users WHERE Email = " . $dbh->quote($email);
	$result = $dbh->query($q);
556
	if (!$result) {
557
		return null;
558
559
	}

560
	$row = $result->fetch(PDO::FETCH_NUM);
Eli Schwartz's avatar
Eli Schwartz committed
561
562
563
	if ($row) {
		return $row[0];
	}
simo's avatar
simo committed
564
565
}

566
567
568
569
570
571
/**
 * Generate clean url with edited/added user values
 *
 * Makes a clean string of variables for use in URLs based on current $_GET and
 * list of values to edit/add to that. Any empty variables are discarded.
 *
572
 * @example print "http://example.com/test.php?" . mkurl("foo=bar&bar=baz")
573
574
 *
 * @param string $append string of variables and values formatted as in URLs
575
 *
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
 * @return string clean string of variables to append to URL, urlencoded
 */
function mkurl($append) {
	$get = $_GET;
	$append = explode('&', $append);
	$uservars = array();
	$out = '';

	foreach ($append as $i) {
		$ex = explode('=', $i);
		$uservars[$ex[0]] = $ex[1];
	}

	foreach ($uservars as $k => $v) { $get[$k] = $v; }

	foreach ($get as $k => $v) {
		if ($v !== '') {
			$out .= '&amp;' . urlencode($k) . '=' . urlencode($v);
		}
	}

	return substr($out, 5);
}
Denis's avatar
Denis committed
599

Marcel Korpel's avatar
Marcel Korpel committed
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
/**
 * Get a package comment
 *
 * @param  int $comment_id The ID of the comment
 *
 * @return array The user ID and comment OR null, null in case of an error
 */
function comment_by_id($comment_id) {
	$dbh = DB::connect();
	$q = "SELECT UsersID, Comments FROM PackageComments ";
	$q.= "WHERE ID = " . intval($comment_id);
	$result = $dbh->query($q);
	if (!$result) {
		return array(null, null);
	}

	return $result->fetch(PDO::FETCH_NUM);
}

619
620
621
622
623
624
625
/**
 * Process submitted comments so any links can be followed
 *
 * @param string $comment Raw user submitted package comment
 *
 * @return string User comment with links printed in HTML
 */
626
function parse_comment($comment) {
627
628
629
630
631
632
633
634
635
636
637
	$url_pattern = '/(\b(?:https?|ftp):\/\/[\w\/\#~:.?+=&%@!\-;,]+?' .
		'(?=[.:?\-;,]*(?:[^\w\/\#~:.?+=&%@!\-;,]|$)))/iS';

	$matches = preg_split($url_pattern, $comment, -1,
		PREG_SPLIT_DELIM_CAPTURE);

	$html = '';
	for ($i = 0; $i < count($matches); $i++) {
		if ($i % 2) {
			# convert links
			$html .= '<a href="' . htmlspecialchars($matches[$i]) .
638
				'" rel="nofollow">' .	htmlspecialchars($matches[$i]) . '</a>';
639
640
641
642
643
644
645
646
647
		}
		else {
			# convert everything else
			$html .= nl2br(htmlspecialchars($matches[$i]));
		}
	}

	return $html;
}
canyonknight's avatar
canyonknight committed
648

649
650
651
/**
 * Wrapper for beginning a database transaction
 */
652
function begin_atomic_commit() {
653
	$dbh = DB::connect();
canyonknight's avatar
canyonknight committed
654
	$dbh->beginTransaction();
canyonknight's avatar
canyonknight committed
655
656
}

657
658
659
/**
 * Wrapper for committing a database transaction
 */
660
function end_atomic_commit() {
661
	$dbh = DB::connect();
canyonknight's avatar
canyonknight committed
662
	$dbh->commit();
canyonknight's avatar
canyonknight committed
663
664
}

665
666
667
668
/**
 * Merge pkgbase and package options
 *
 * Merges entries of the first and the second array. If any key appears in both
669
670
671
672
673
674
 * arrays and the corresponding value in the second array is either a non-array
 * type or a non-empty array, the value from the second array replaces the
 * value from the first array. If the value from the second array is an array
 * containing a single empty string, the value in the resulting array becomes
 * an empty array instead. If the value in the second array is empty, the
 * resulting array contains the value from the first array.
675
676
677
678
679
680
681
682
683
684
 *
 * @param array $pkgbase_info Options from the pkgbase section
 * @param array $section_info Options from the package section
 *
 * @return array Merged information from both sections
 */
function array_pkgbuild_merge($pkgbase_info, $section_info) {
	$pi = $pkgbase_info;
	foreach ($section_info as $opt_key => $opt_val) {
		if (is_array($opt_val)) {
685
686
687
688
689
			if ($opt_val == array('')) {
				$pi[$opt_key] = array();
			} elseif (count($opt_val) > 0) {
				$pi[$opt_key] = $opt_val;
			}
690
691
692
693
694
695
		} else {
			$pi[$opt_key] = $opt_val;
		}
	}
	return $pi;
}
696
697
698
699
700
701
702
703
704
705
706
707
708

/**
 * Bound an integer value between two values
 *
 * @param int $n Integer value to bound
 * @param int $min Lower bound
 * @param int $max Upper bound
 *
 * @return int Bounded integer value
 */
function bound($n, $min, $max) {
	return min(max($n, $min), $max);
}
709
710
711
712
713
714
715
716
717
718
719
720
721

/**
 * Return the URL of the AUR root
 *
 * @return string The URL of the AUR root
 */
function aur_location() {
	$location = config_get('options', 'aur_location');
	if (substr($location, -1) != '/') {
		$location .= '/';
	}
	return $location;
}
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774

/**
 * Calculate pagination templates
 *
 * @return array The array of pagination templates, per page, and offset values
 */
function calculate_pagination($total_comment_count) {
	/* Sanitize paging variables. */
	if (isset($_GET["O"])) {
		$_GET["O"] = max(intval($_GET["O"]), 0);
	} else {
		$_GET["O"] = 0;
	}
	$offset = $_GET["O"];

	if (isset($_GET["PP"])) {
		$_GET["PP"] = bound(intval($_GET["PP"]), 1, 250);
	} else {
		$_GET["PP"] = 10;
	}
	$per_page = $_GET["PP"];

	// Page offsets start at zero, so page 2 has offset 1, which means that we
	// need to add 1 to the offset to get the current page.
	$current_page = ceil($offset / $per_page) + 1;
	$num_pages = ceil($total_comment_count / $per_page);
	$pagination_templs = array();

	if ($current_page > 1) {
		$previous_page = $current_page - 1;
		$previous_offset = ($previous_page - 1) * $per_page;
		$pagination_templs['&laquo; ' . __('First')] = 0;
		$pagination_templs['&lsaquo; ' . __('Previous')] = $previous_offset;
	}

	if ($current_page - 5 > 1) {
		$pagination_templs["..."] = false;
	}

	for ($i = max($current_page - 5, 1); $i <= min($num_pages, $current_page + 5); $i++) {
		$pagination_templs[$i] = ($i - 1) * $per_page;
	}

	if ($current_page + 5 < $num_pages)
		$pagination_templs["... "] = false;

	if ($current_page < $num_pages) {
		$pagination_templs[__('Next') . ' &rsaquo;'] = $current_page * $per_page;
		$pagination_templs[__('Last') . ' &raquo;'] = ($num_pages - 1) * $per_page;
	}

	return array($pagination_templs, $per_page, $offset);
}