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

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("config.inc.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");
elij's avatar
elij committed
18

19
20
21
22
23
24
25
26
27
28
29
30
31
/**
 * 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
 * @global string $LOGIN_TIMEOUT Time until session times out
 * @param \PDO $dbh Already established database connection
 *
 * @return void
 */
32
function check_sid($dbh=NULL) {
eric's avatar
eric committed
33
	global $_COOKIE;
eric's avatar
eric committed
34
	global $LOGIN_TIMEOUT;
eric's avatar
eric committed
35

Loui Chang's avatar
Loui Chang committed
36
	if (isset($_COOKIE["AURSID"])) {
eric's avatar
eric committed
37
38
39
		$failed = 0;
		# the visitor is logged in, try and update the session
		#
40
41
42
		if(!$dbh) {
			$dbh = db_connect();
		}
eric's avatar
eric committed
43
		$q = "SELECT LastUpdateTS, UNIX_TIMESTAMP() FROM Sessions ";
canyonknight's avatar
canyonknight committed
44
45
46
47
48
		$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
49
50
			# Invalid SessionID - hacker alert!
			#
eric's avatar
eric committed
51
52
			$failed = 1;
		} else {
53
54
			$last_update = $row[0];
			if ($last_update + $LOGIN_TIMEOUT <= $row[1]) {
eric's avatar
eric committed
55
				$failed = 2;
eric's avatar
eric committed
56
57
			}
		}
58

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

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

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

91
92
93
94
95
/**
 * Verify the supplied CSRF token matches expected token
 *
 * @return bool True if the CSRF token is the same as the cookie SID, otherwise false
 */
96
97
98
99
100
101
102
103
function check_token() {
	if (isset($_POST['token'])) {
		return ($_POST['token'] == $_COOKIE['AURSID']);
	} else {
		return false;
	}
}

104
105
106
107
108
109
110
/**
 * 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
111
function valid_email($addy) {
112
113
114
115
116
117
118
119
120
121
122
123
	// 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
124
125
}

126
127
128
129
130
/**
 * Generate a unique session ID
 *
 * @return string MD5 hash of the concatenated user IP, random number, and current time
 */
eric's avatar
eric committed
131
function new_sid() {
132
	return md5($_SERVER['REMOTE_ADDR'] . uniqid(mt_rand(), true));
eric's avatar
eric committed
133
134
}

135
136
137
138
139
140
141
142
/**
 * Determine the user's username in the database using a user ID
 *
 * @param string $id User's ID
 * @param \PDO $dbh Already established database connection
 *
 * @return string Username if it exists, otherwise "None"
 */
143
function username_from_id($id="", $dbh=NULL) {
144
145
146
	if (!$id) {
		return "";
	}
147
148
149
	if(!$dbh) {
		$dbh = db_connect();
	}
canyonknight's avatar
canyonknight committed
150
151
	$q = "SELECT Username FROM Users WHERE ID = " . $dbh->quote($id);
	$result = $dbh->query($q);
152
153
154
	if (!$result) {
		return "None";
	}
canyonknight's avatar
canyonknight committed
155
	$row = $result->fetch(PDO::FETCH_NUM);
156
157
158
159

	return $row[0];
}

160
161
162
163
164
165
166
167
/**
 * Determine the user's username in the database using a session ID
 *
 * @param string $sid User's session ID
 * @param \PDO $dbh Already established database connection
 *
 * @return string Username of the visitor
 */
168
function username_from_sid($sid="", $dbh=NULL) {
eric's avatar
eric committed
169
170
171
	if (!$sid) {
		return "";
	}
172
173
174
	if(!$dbh) {
		$dbh = db_connect();
	}
eric's avatar
eric committed
175
176
177
	$q = "SELECT Username ";
	$q.= "FROM Users, Sessions ";
	$q.= "WHERE Users.ID = Sessions.UsersID ";
canyonknight's avatar
canyonknight committed
178
179
	$q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
	$result = $dbh->query($q);
eric's avatar
eric committed
180
181
182
	if (!$result) {
		return "";
	}
canyonknight's avatar
canyonknight committed
183
	$row = $result->fetch(PDO::FETCH_NUM);
eric's avatar
eric committed
184
185
186
187

	return $row[0];
}

188
189
190
191
192
193
194
195
/**
 * Determine the user's e-mail address in the database using a session ID
 *
 * @param string $sid User's session ID
 * @param \PDO $dbh Already established database connection
 *
 * @return string User's e-mail address as given during registration
 */
196
function email_from_sid($sid="", $dbh=NULL) {
eric's avatar
eric committed
197
198
199
	if (!$sid) {
		return "";
	}
200
201
202
	if(!$dbh) {
		$dbh = db_connect();
	}
eric's avatar
eric committed
203
204
205
	$q = "SELECT Email ";
	$q.= "FROM Users, Sessions ";
	$q.= "WHERE Users.ID = Sessions.UsersID ";
canyonknight's avatar
canyonknight committed
206
207
	$q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
	$result = $dbh->query($q);
eric's avatar
eric committed
208
209
210
	if (!$result) {
		return "";
	}
canyonknight's avatar
canyonknight committed
211
	$row = $result->fetch(PDO::FETCH_NUM);
eric's avatar
eric committed
212
213
214
215

	return $row[0];
}

216
217
218
219
220
221
222
223
/**
 * Determine the user's account type in the database using a session ID
 *
 * @param string $sid User's session ID
 * @param \PDO $dbh Already established database connection
 *
 * @return string Account type of user ("User", "Trusted User", or "Developer")
 */
224
function account_from_sid($sid="", $dbh=NULL) {
eric's avatar
eric committed
225
226
227
	if (!$sid) {
		return "";
	}
228
229
230
	if(!$dbh) {
		$dbh = db_connect();
	}
eric's avatar
eric committed
231
232
233
	$q = "SELECT AccountType ";
	$q.= "FROM Users, AccountTypes, Sessions ";
	$q.= "WHERE Users.ID = Sessions.UsersID ";
234
	$q.= "AND AccountTypes.ID = Users.AccountTypeID ";
canyonknight's avatar
canyonknight committed
235
236
	$q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
	$result = $dbh->query($q);
eric's avatar
eric committed
237
238
239
	if (!$result) {
		return "";
	}
canyonknight's avatar
canyonknight committed
240
	$row = $result->fetch(PDO::FETCH_NUM);
eric's avatar
eric committed
241
242
243

	return $row[0];
}
244

245
246
247
248
249
250
251
252
/**
 * Determine the user's ID in the database using a session ID
 *
 * @param string $sid User's session ID
 * @param \PDO $dbh Already established database connection
 *
 * @return string|int The user's name, 0 on query failure
 */
253
function uid_from_sid($sid="", $dbh=NULL) {
254
255
256
	if (!$sid) {
		return "";
	}
257
258
259
	if(!$dbh) {
		$dbh = db_connect();
	}
260
261
262
	$q = "SELECT Users.ID ";
	$q.= "FROM Users, Sessions ";
	$q.= "WHERE Users.ID = Sessions.UsersID ";
canyonknight's avatar
canyonknight committed
263
264
	$q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
	$result = $dbh->query($q);
265
266
267
	if (!$result) {
		return 0;
	}
canyonknight's avatar
canyonknight committed
268
	$row = $result->fetch(PDO::FETCH_NUM);
269
270
271
272

	return $row[0];
}

273
274
275
276
277
/**
 * Establish a connection with a database using PDO
 *
 * @return \PDO A database connection
 */
278
function db_connect() {
canyonknight's avatar
canyonknight committed
279
280
	try {
		$dbh = new PDO(AUR_db_DSN_prefix . ":" . AUR_db_host . ";dbname=" . AUR_db_name, AUR_db_user, AUR_db_pass);
Dan McGee's avatar
Dan McGee committed
281
	}
canyonknight's avatar
canyonknight committed
282
283
	catch (PDOException $e) {
		echo "Error - Could not connect to AUR database: " . $e->getMessage();
eric's avatar
eric committed
284
	}
285

canyonknight's avatar
canyonknight committed
286
	$dbh->exec("SET NAMES 'utf8' COLLATE 'utf8_general_ci';");
287

canyonknight's avatar
canyonknight committed
288
	return $dbh;
eric's avatar
eric committed
289
290
}

291
292
293
294
295
296
297
298
299
/**
 * 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
 */
300
function html_header($title="") {
301
302
	global $AUR_LOCATION;
	global $DISABLE_HTTP_LOGIN;
303
	global $LANG;
304
	global $SUPPORTED_LANGS;
305

306
307
	include('header.php');
	return;
308
309
}

310
311
312
313
314
315
316
/**
 * Common AUR footer displayed on all pages
 *
 * @param string $ver The AUR version
 *
 * @return void
 */
317
function html_footer($ver="") {
318
	include('footer.php');
319
320
321
	return;
}

322
323
324
325
326
327
328
329
330
/**
 * 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
 * @param \PDO $dbh Already established database connection
 *
 * @return int 0 if the user can't submit, 1 if the user can submit
 */
331
function can_submit_pkg($name="", $sid="", $dbh=NULL) {
eric's avatar
eric committed
332
	if (!$name || !$sid) {return 0;}
333
334
335
	if(!$dbh) {
		$dbh = db_connect();
	}
Dan McGee's avatar
Dan McGee committed
336
	$q = "SELECT MaintainerUID ";
canyonknight's avatar
canyonknight committed
337
338
339
340
341
342
343
	$q.= "FROM Packages WHERE Name = " . $dbh->quote($name);
	$result = $dbh->query($q);
	$row = $result->fetch(PDO::FETCH_NUM);

	if (!$row[0]) {
		return 1;
	}
344
	$my_uid = uid_from_sid($sid, $dbh);
eric's avatar
eric committed
345

346
	if ($row[0] === NULL || $row[0] == $my_uid) {
347
348
		return 1;
	}
eric's avatar
eric committed
349
350
351
352

	return 0;
}

353
354
355
356
357
358
359
/**
 * Recursively delete a directory
 *
 * @param string $dirname Name of the directory to be removed
 *
 * @return void
 */
360
361
362
363
364
365
366
367
368
369
370
371
372
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
373
	}
Loui Chang's avatar
Loui Chang committed
374

375
376
	rmdir($dirname);

eric's avatar
eric committed
377
378
379
	return;
}

380
381
382
383
384
385
386
387
 /**
 * Determine the user's ID in the database using a username
 *
 * @param string $username The username of an account
 * @param \PDO $dbh Already established database connection
 *
 * @return string Return user ID if exists for username, otherwise "None"
 */
388
function uid_from_username($username="", $dbh=NULL) {
simo's avatar
simo committed
389
390
391
	if (!$username) {
		return "";
	}
392
393
394
	if(!$dbh) {
		$dbh = db_connect();
	}
canyonknight's avatar
canyonknight committed
395
396
	$q = "SELECT ID FROM Users WHERE Username = " . $dbh->quote($username);
	$result = $dbh->query($q);
simo's avatar
simo committed
397
398
399
	if (!$result) {
		return "None";
	}
canyonknight's avatar
canyonknight committed
400
	$row = $result->fetch(PDO::FETCH_NUM);
401
402
403
404

	return $row[0];
}

405
406
407
408
409
410
411
412
/**
 * 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
 * @param \PDO $dbh Already established database connection
 *
 * @return string The user's ID
 */
413
function uid_from_email($email="", $dbh=NULL) {
414
415
416
	if (!$email) {
		return "";
	}
417
418
419
	if(!$dbh) {
		$dbh = db_connect();
	}
canyonknight's avatar
canyonknight committed
420
421
	$q = "SELECT ID FROM Users WHERE Email = " . $dbh->quote($email);
	$result = $dbh->query($q);
422
423
424
	if (!$result) {
		return "None";
	}
canyonknight's avatar
canyonknight committed
425
	$row = $result->fetch(PDO::FETCH_NUM);
426

simo's avatar
simo committed
427
428
429
	return $row[0];
}

430
431
432
433
434
/**
 * Determine if a user has TU or Developer privileges
 *
 * @return bool Return true if the user is a TU or developer, otherwise false
 */
435
function check_user_privileges() {
436
437
438
439
	$type = account_from_sid($_COOKIE['AURSID']);
	return ($type == 'Trusted User' || $type == 'Developer');
}

440
441
442
443
444
445
/**
 * 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.
 *
446
 * @example print "http://example.com/test.php?" . mkurl("foo=bar&bar=baz")
447
448
 *
 * @param string $append string of variables and values formatted as in URLs
449
 *
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
 * @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
473

474
475
476
477
478
479
480
481
/**
 * Determine a user's salt from the database
 *
 * @param string $user_id The user ID of the user trying to log in
 * @param \PDO $dbh Already established database connection
 *
 * @return string|void Return the salt for the requested user, otherwise void
 */
482
function get_salt($user_id, $dbh=NULL) {
483
484
485
	if(!$dbh) {
		$dbh = db_connect();
	}
canyonknight's avatar
canyonknight committed
486
487
	$q = "SELECT Salt FROM Users WHERE ID = " . $user_id;
	$result = $dbh->query($q);
elij's avatar
elij committed
488
	if ($result) {
canyonknight's avatar
canyonknight committed
489
490
		$row = $result->fetch(PDO::FETCH_NUM);
		return $row[0];
elij's avatar
elij committed
491
492
	}
	return;
Denis's avatar
Denis committed
493
494
}

495
496
497
498
499
500
501
/**
 * Save a user's salted password in the database
 *
 * @param string $user_id The user ID of the user who is salting their password
 * @param string $passwd The password of the user logging in
 * @param \PDO $dbh Already established database connection
 */
502
function save_salt($user_id, $passwd, $dbh=NULL) {
503
504
505
	if(!$dbh) {
		$dbh = db_connect();
	}
Denis's avatar
Denis committed
506
507
	$salt = generate_salt();
	$hash = salted_hash($passwd, $salt);
canyonknight's avatar
canyonknight committed
508
509
510
	$q = "UPDATE Users SET Salt = " . $dbh->quote($salt) . ", ";
	$q.= "Passwd = " . $dbh->quote($hash) . " WHERE ID = " . $user_id;
	$result = $dbh->exec($q);
Denis's avatar
Denis committed
511
512
}

513
514
515
516
517
/**
 * Generate a string to be used for salting passwords
 *
 * @return string MD5 hash of concatenated random number and current time
 */
518
function generate_salt() {
519
	return md5(uniqid(mt_rand(), true));
Denis's avatar
Denis committed
520
521
}

522
523
524
525
526
527
528
529
/**
 * Combine salt and password to form a hash
 *
 * @param string $passwd User plaintext password
 * @param string $salt MD5 hash to be used as user salt
 *
 * @return string The MD5 hash of the concatenated salt and user password
 */
530
function salted_hash($passwd, $salt) {
Denis's avatar
Denis committed
531
532
533
534
535
	if (strlen($salt) != 32) {
		trigger_error('Salt does not look like an md5 hash', E_USER_WARNING);
	}
	return md5($salt . $passwd);
}
536

537
538
539
540
541
542
543
/**
 * 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
 */
544
function parse_comment($comment) {
545
546
547
548
549
550
551
552
553
554
555
	$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]) .
elij's avatar
elij committed
556
				'">' .	htmlspecialchars($matches[$i]) . '</a>';
557
558
559
560
561
562
563
564
565
		}
		else {
			# convert everything else
			$html .= nl2br(htmlspecialchars($matches[$i]));
		}
	}

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

567
568
569
570
571
/**
 * Wrapper for beginning a database transaction
 *
 * @param \PDO $dbh Already established database connection
 */
canyonknight's avatar
canyonknight committed
572
573
574
575
function begin_atomic_commit($dbh=NULL) {
	if(!$dbh) {
		$dbh = db_connect();
	}
canyonknight's avatar
canyonknight committed
576
	$dbh->beginTransaction();
canyonknight's avatar
canyonknight committed
577
578
}

579
580
581
582
583
/**
 * Wrapper for committing a database transaction
 *
 * @param \PDO $dbh Already established database connection
 */
canyonknight's avatar
canyonknight committed
584
585
586
587
function end_atomic_commit($dbh=NULL) {
	if(!$dbh) {
		$dbh = db_connect();
	}
canyonknight's avatar
canyonknight committed
588
	$dbh->commit();
canyonknight's avatar
canyonknight committed
589
590
}

591
592
593
594
595
596
597
598
/**
 *
 * Determine the row ID for the most recently insterted row
 *
 * @param \PDO $dbh Already established database connection
 *
 * @return string The ID of the last inserted row
 */
canyonknight's avatar
canyonknight committed
599
600
601
602
function last_insert_id($dbh=NULL) {
	if(!$dbh) {
		$dbh = db_connect();
	}
canyonknight's avatar
canyonknight committed
603
	return $dbh->lastInsertId();
canyonknight's avatar
canyonknight committed
604
}
canyonknight's avatar
canyonknight committed
605

606
607
608
609
610
611
612
613
/**
 * Determine package information for latest package
 *
 * @param int $numpkgs Number of packages to get information on
 * @param \PDO $dbh Already established database connection
 *
 * @return array $packages Package info for the specified number of recent packages
 */
canyonknight's avatar
canyonknight committed
614
615
616
617
618
619
620
621
function latest_pkgs($numpkgs, $dbh=NULL) {
	if(!$dbh) {
		$dbh = db_connect();
	}

	$q = "SELECT * FROM Packages ";
	$q.= "ORDER BY SubmittedTS DESC ";
	$q.= "LIMIT " .intval($numpkgs);
canyonknight's avatar
canyonknight committed
622
	$result = $dbh->query($q);
canyonknight's avatar
canyonknight committed
623
624

	if ($result) {
canyonknight's avatar
canyonknight committed
625
		while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
canyonknight's avatar
canyonknight committed
626
627
628
629
630
631
			$packages[] = $row;
		}
	}

	return $packages;
}