aur.inc.php 16.7 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

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

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

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

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

24
check_sid();
25
check_tos();
26

27
28
29
30
31
32
33
34
35
36
37
/**
 * 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
 */
38
function check_sid() {
eric's avatar
eric committed
39
40
	global $_COOKIE;

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

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

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

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

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

95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/**
 * 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();
	}
}

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

130
131
132
133
134
135
136
/**
 * 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
137
function valid_email($addy) {
138
139
140
141
142
143
144
145
146
147
148
149
	// 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
150
151
}

152
153
154
155
156
/**
 * Generate a unique session ID
 *
 * @return string MD5 hash of the concatenated user IP, random number, and current time
 */
eric's avatar
eric committed
157
function new_sid() {
158
	return md5($_SERVER['REMOTE_ADDR'] . uniqid(mt_rand(), true));
eric's avatar
eric committed
159
160
}

161
162
163
164
165
/**
 * Determine the user's username in the database using a user ID
 *
 * @param string $id User's ID
 *
166
 * @return string Username if it exists, otherwise null
167
 */
168
169
170
function username_from_id($id) {
	$id = intval($id);

171
	$dbh = DB::connect();
canyonknight's avatar
canyonknight committed
172
173
	$q = "SELECT Username FROM Users WHERE ID = " . $dbh->quote($id);
	$result = $dbh->query($q);
174
	if (!$result) {
175
		return null;
176
177
	}

178
	$row = $result->fetch(PDO::FETCH_NUM);
179
180
181
	return $row[0];
}

182
183
184
185
186
187
188
/**
 * 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
 */
189
function username_from_sid($sid="") {
eric's avatar
eric committed
190
191
192
	if (!$sid) {
		return "";
	}
193
	$dbh = DB::connect();
eric's avatar
eric committed
194
195
196
	$q = "SELECT Username ";
	$q.= "FROM Users, Sessions ";
	$q.= "WHERE Users.ID = Sessions.UsersID ";
canyonknight's avatar
canyonknight committed
197
198
	$q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
	$result = $dbh->query($q);
eric's avatar
eric committed
199
200
201
	if (!$result) {
		return "";
	}
canyonknight's avatar
canyonknight committed
202
	$row = $result->fetch(PDO::FETCH_NUM);
eric's avatar
eric committed
203
204
205
206

	return $row[0];
}

207
208
209
210
211
/**
 * Format a user name for inclusion in HTML data
 *
 * @param string $username The user name to format
 *
212
 * @return string The generated HTML code for the account link
213
214
 */
function html_format_username($username) {
215
216
	$username_fmt = $username ? htmlspecialchars($username, ENT_QUOTES) : __("None");

217
	if ($username && isset($_COOKIE["AURSID"])) {
218
219
220
221
222
223
224
		$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;
	}
225
226
}

227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/**
 * 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;
}

252
253
254
255
/**
 * Format a link in the package actions box
 *
 * @param string $uri The link target
256
 * @param string $inner The HTML code to use for the link label
257
258
259
 *
 * @return string The generated HTML code for the action link
 */
260
function html_action_link($uri, $inner) {
261
262
263
264
265
266
	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) . '">';
	}
267
	$code .= $inner . '</a>';
268
269
270
271
272
273
274
275
276

	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)
277
 * @param string $inner The HTML code to use for the link label
278
279
280
 *
 * @return string The generated HTML code for the action link
 */
281
function html_action_form($uri, $action, $inner) {
282
283
284
285
286
287
288
	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) . '" ';
289
		$code .= 'value="' . $inner . '" />';
290
291
292
		$code .= '</form>';
	} else {
		$code = '<a href="' . get_uri('/login/', true) . '">';
293
		$code .= $inner . '</a>';
294
	}
295
296
297
298

	return $code;
}

299
300
301
302
303
304
305
/**
 * 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
 */
306
function email_from_sid($sid="") {
eric's avatar
eric committed
307
308
309
	if (!$sid) {
		return "";
	}
310
	$dbh = DB::connect();
eric's avatar
eric committed
311
312
313
	$q = "SELECT Email ";
	$q.= "FROM Users, Sessions ";
	$q.= "WHERE Users.ID = Sessions.UsersID ";
canyonknight's avatar
canyonknight committed
314
315
	$q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
	$result = $dbh->query($q);
eric's avatar
eric committed
316
317
318
	if (!$result) {
		return "";
	}
canyonknight's avatar
canyonknight committed
319
	$row = $result->fetch(PDO::FETCH_NUM);
eric's avatar
eric committed
320
321
322
323

	return $row[0];
}

324
325
326
327
328
329
330
/**
 * 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")
 */
331
function account_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 AccountType ";
	$q.= "FROM Users, AccountTypes, Sessions ";
	$q.= "WHERE Users.ID = Sessions.UsersID ";
339
	$q.= "AND AccountTypes.ID = Users.AccountTypeID ";
canyonknight's avatar
canyonknight committed
340
341
	$q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
	$result = $dbh->query($q);
eric's avatar
eric committed
342
343
344
	if (!$result) {
		return "";
	}
canyonknight's avatar
canyonknight committed
345
	$row = $result->fetch(PDO::FETCH_NUM);
eric's avatar
eric committed
346
347
348

	return $row[0];
}
349

350
351
352
353
354
355
356
/**
 * 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
 */
357
function uid_from_sid($sid="") {
358
359
360
	if (!$sid) {
		return "";
	}
361
	$dbh = DB::connect();
362
363
364
	$q = "SELECT Users.ID ";
	$q.= "FROM Users, Sessions ";
	$q.= "WHERE Users.ID = Sessions.UsersID ";
canyonknight's avatar
canyonknight committed
365
366
	$q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
	$result = $dbh->query($q);
367
368
369
	if (!$result) {
		return 0;
	}
canyonknight's avatar
canyonknight committed
370
	$row = $result->fetch(PDO::FETCH_NUM);
371
372
373
374

	return $row[0];
}

375
376
377
378
379
380
381
382
383
/**
 * 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
 */
384
function html_header($title="", $details=array()) {
385
	global $LANG;
386
	global $SUPPORTED_LANGS;
387

388
389
	include('header.php');
	return;
390
391
}

392
393
394
395
396
397
398
/**
 * Common AUR footer displayed on all pages
 *
 * @param string $ver The AUR version
 *
 * @return void
 */
399
function html_footer($ver="") {
400
	include('footer.php');
401
402
403
	return;
}

404
405
406
407
408
409
410
411
/**
 * 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
 */
412
function can_submit_pkgbase($name="", $sid="") {
eric's avatar
eric committed
413
	if (!$name || !$sid) {return 0;}
414
	$dbh = DB::connect();
Dan McGee's avatar
Dan McGee committed
415
	$q = "SELECT MaintainerUID ";
416
	$q.= "FROM PackageBases WHERE Name = " . $dbh->quote($name);
canyonknight's avatar
canyonknight committed
417
418
419
420
421
422
	$result = $dbh->query($q);
	$row = $result->fetch(PDO::FETCH_NUM);

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

425
	if ($row[0] === NULL || $row[0] == $my_uid) {
426
427
		return 1;
	}
eric's avatar
eric committed
428
429
430
431

	return 0;
}

432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
/**
 * 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);
}

451
452
453
454
455
456
457
/**
 * Recursively delete a directory
 *
 * @param string $dirname Name of the directory to be removed
 *
 * @return void
 */
458
459
460
461
462
463
464
465
466
467
468
469
470
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
471
	}
Loui Chang's avatar
Loui Chang committed
472

473
474
	rmdir($dirname);

eric's avatar
eric committed
475
476
477
	return;
}

478
479
480
481
482
 /**
 * Determine the user's ID in the database using a username
 *
 * @param string $username The username of an account
 *
483
 * @return string Return user ID if exists for username, otherwise null
484
 */
485
function uid_from_username($username) {
486
	$dbh = DB::connect();
canyonknight's avatar
canyonknight committed
487
488
	$q = "SELECT ID FROM Users WHERE Username = " . $dbh->quote($username);
	$result = $dbh->query($q);
simo's avatar
simo committed
489
	if (!$result) {
490
		return null;
simo's avatar
simo committed
491
	}
492

493
	$row = $result->fetch(PDO::FETCH_NUM);
494
495
496
	return $row[0];
}

497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
/**
 * 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;
}

512
513
514
515
516
517
518
/**
 * 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
 */
519
function uid_from_email($email) {
520
	$dbh = DB::connect();
canyonknight's avatar
canyonknight committed
521
522
	$q = "SELECT ID FROM Users WHERE Email = " . $dbh->quote($email);
	$result = $dbh->query($q);
523
	if (!$result) {
524
		return null;
525
526
	}

527
	$row = $result->fetch(PDO::FETCH_NUM);
simo's avatar
simo committed
528
529
530
	return $row[0];
}

531
532
533
534
535
536
/**
 * 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.
 *
537
 * @example print "http://example.com/test.php?" . mkurl("foo=bar&bar=baz")
538
539
 *
 * @param string $append string of variables and values formatted as in URLs
540
 *
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
 * @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
564

Marcel Korpel's avatar
Marcel Korpel committed
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
/**
 * 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);
}

584
585
586
587
588
589
590
/**
 * 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
 */
591
function parse_comment($comment) {
592
593
594
595
596
597
598
599
600
601
602
	$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]) .
603
				'" rel="nofollow">' .	htmlspecialchars($matches[$i]) . '</a>';
604
605
606
607
608
609
610
611
612
		}
		else {
			# convert everything else
			$html .= nl2br(htmlspecialchars($matches[$i]));
		}
	}

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

614
615
616
/**
 * Wrapper for beginning a database transaction
 */
617
function begin_atomic_commit() {
618
	$dbh = DB::connect();
canyonknight's avatar
canyonknight committed
619
	$dbh->beginTransaction();
canyonknight's avatar
canyonknight committed
620
621
}

622
623
624
/**
 * Wrapper for committing a database transaction
 */
625
function end_atomic_commit() {
626
	$dbh = DB::connect();
canyonknight's avatar
canyonknight committed
627
	$dbh->commit();
canyonknight's avatar
canyonknight committed
628
629
}

630
631
632
633
/**
 * Merge pkgbase and package options
 *
 * Merges entries of the first and the second array. If any key appears in both
634
635
636
637
638
639
 * 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.
640
641
642
643
644
645
646
647
648
649
 *
 * @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)) {
650
651
652
653
654
			if ($opt_val == array('')) {
				$pi[$opt_key] = array();
			} elseif (count($opt_val) > 0) {
				$pi[$opt_key] = $opt_val;
			}
655
656
657
658
659
660
		} else {
			$pi[$opt_key] = $opt_val;
		}
	}
	return $pi;
}
661
662
663
664
665
666
667
668
669
670
671
672
673

/**
 * 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);
}
674
675
676
677
678
679
680
681
682
683
684
685
686

/**
 * 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;
}