Remove double htmlspecialchars
[aur.git] / web / lib / aur.inc.php
blob387d81de0578883d47ee6dcdc2f22520d2da7f25
1 <?php
2 set_include_path(get_include_path() . PATH_SEPARATOR . '../lib' . PATH_SEPARATOR . '../template');
3 header('Content-Type: text/html; charset=utf-8');
4 header('Cache-Control: no-cache, must-revalidate');
5 header('Expires: Tue, 11 Oct 1988 22:00:00 GMT'); // quite a special day
6 header('Pragma: no-cache');
8 date_default_timezone_set('UTC');
10 include_once('translator.inc.php');
11 set_lang();
13 include_once("config.inc.php");
14 include_once("routing.inc.php");
15 include_once("version.inc.php");
16 include_once("acctfuncs.inc.php");
17 include_once("cachefuncs.inc.php");
19 /**
20 * Check if a visitor is logged in
22 * Query "Sessions" table with supplied cookie. Determine if the cookie is valid
23 * or not. Unset the cookie if invalid or session timeout reached. Update the
24 * session timeout if it is still valid.
26 * @global array $_COOKIE User cookie values
27 * @global string $LOGIN_TIMEOUT Time until session times out
28 * @param \PDO $dbh Already established database connection
30 * @return void
32 function check_sid($dbh=NULL) {
33 global $_COOKIE;
34 global $LOGIN_TIMEOUT;
36 if (isset($_COOKIE["AURSID"])) {
37 $failed = 0;
38 # the visitor is logged in, try and update the session
40 if(!$dbh) {
41 $dbh = db_connect();
43 $q = "SELECT LastUpdateTS, UNIX_TIMESTAMP() FROM Sessions ";
44 $q.= "WHERE SessionID = " . $dbh->quote($_COOKIE["AURSID"]);
45 $result = $dbh->query($q);
46 $row = $result->fetch(PDO::FETCH_NUM);
48 if (!$row[0]) {
49 # Invalid SessionID - hacker alert!
51 $failed = 1;
52 } else {
53 $last_update = $row[0];
54 if ($last_update + $LOGIN_TIMEOUT <= $row[1]) {
55 $failed = 2;
59 if ($failed == 1) {
60 # 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!?
63 setcookie("AURSID", "", 1, "/", null, !empty($_SERVER['HTTPS']), true);
64 unset($_COOKIE['AURSID']);
65 } elseif ($failed == 2) {
66 # session id timeout was reached and they must login again.
68 delete_session_id($_COOKIE["AURSID"], $dbh);
70 setcookie("AURSID", "", 1, "/", null, !empty($_SERVER['HTTPS']), true);
71 unset($_COOKIE['AURSID']);
72 } else {
73 # still logged in and haven't reached the timeout, go ahead
74 # and update the idle timestamp
76 # Only update the timestamp if it is less than the
77 # current time plus $LOGIN_TIMEOUT.
79 # This keeps 'remembered' sessions from being
80 # overwritten.
81 if ($last_update < time() + $LOGIN_TIMEOUT) {
82 $q = "UPDATE Sessions SET LastUpdateTS = UNIX_TIMESTAMP() ";
83 $q.= "WHERE SessionID = " . $dbh->quote($_COOKIE["AURSID"]);
84 $dbh->exec($q);
88 return;
91 /**
92 * Verify the supplied CSRF token matches expected token
94 * @return bool True if the CSRF token is the same as the cookie SID, otherwise false
96 function check_token() {
97 if (isset($_POST['token'])) {
98 return ($_POST['token'] == $_COOKIE['AURSID']);
99 } else {
100 return false;
105 * Verify a user supplied e-mail against RFC 3696 and DNS records
107 * @param string $addy E-mail address being validated in foo@example.com format
109 * @return bool True if e-mail passes validity checks, otherwise false
111 function valid_email($addy) {
112 // check against RFC 3696
113 if (filter_var($addy, FILTER_VALIDATE_EMAIL) === false) {
114 return false;
117 // check dns for mx, a, aaaa records
118 list($local, $domain) = explode('@', $addy);
119 if (!(checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A') || checkdnsrr($domain, 'AAAA'))) {
120 return false;
123 return true;
127 * Generate a unique session ID
129 * @return string MD5 hash of the concatenated user IP, random number, and current time
131 function new_sid() {
132 return md5($_SERVER['REMOTE_ADDR'] . uniqid(mt_rand(), true));
136 * Determine the user's username in the database using a user ID
138 * @param string $id User's ID
139 * @param \PDO $dbh Already established database connection
141 * @return string Username if it exists, otherwise "None"
143 function username_from_id($id="", $dbh=NULL) {
144 if (!$id) {
145 return "";
147 if(!$dbh) {
148 $dbh = db_connect();
150 $q = "SELECT Username FROM Users WHERE ID = " . $dbh->quote($id);
151 $result = $dbh->query($q);
152 if (!$result) {
153 return "None";
155 $row = $result->fetch(PDO::FETCH_NUM);
157 return $row[0];
161 * Determine the user's username in the database using a session ID
163 * @param string $sid User's session ID
164 * @param \PDO $dbh Already established database connection
166 * @return string Username of the visitor
168 function username_from_sid($sid="", $dbh=NULL) {
169 if (!$sid) {
170 return "";
172 if(!$dbh) {
173 $dbh = db_connect();
175 $q = "SELECT Username ";
176 $q.= "FROM Users, Sessions ";
177 $q.= "WHERE Users.ID = Sessions.UsersID ";
178 $q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
179 $result = $dbh->query($q);
180 if (!$result) {
181 return "";
183 $row = $result->fetch(PDO::FETCH_NUM);
185 return $row[0];
189 * Determine the user's e-mail address in the database using a session ID
191 * @param string $sid User's session ID
192 * @param \PDO $dbh Already established database connection
194 * @return string User's e-mail address as given during registration
196 function email_from_sid($sid="", $dbh=NULL) {
197 if (!$sid) {
198 return "";
200 if(!$dbh) {
201 $dbh = db_connect();
203 $q = "SELECT Email ";
204 $q.= "FROM Users, Sessions ";
205 $q.= "WHERE Users.ID = Sessions.UsersID ";
206 $q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
207 $result = $dbh->query($q);
208 if (!$result) {
209 return "";
211 $row = $result->fetch(PDO::FETCH_NUM);
213 return $row[0];
217 * Determine the user's account type in the database using a session ID
219 * @param string $sid User's session ID
220 * @param \PDO $dbh Already established database connection
222 * @return string Account type of user ("User", "Trusted User", or "Developer")
224 function account_from_sid($sid="", $dbh=NULL) {
225 if (!$sid) {
226 return "";
228 if(!$dbh) {
229 $dbh = db_connect();
231 $q = "SELECT AccountType ";
232 $q.= "FROM Users, AccountTypes, Sessions ";
233 $q.= "WHERE Users.ID = Sessions.UsersID ";
234 $q.= "AND AccountTypes.ID = Users.AccountTypeID ";
235 $q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
236 $result = $dbh->query($q);
237 if (!$result) {
238 return "";
240 $row = $result->fetch(PDO::FETCH_NUM);
242 return $row[0];
246 * Determine the user's ID in the database using a session ID
248 * @param string $sid User's session ID
249 * @param \PDO $dbh Already established database connection
251 * @return string|int The user's name, 0 on query failure
253 function uid_from_sid($sid="", $dbh=NULL) {
254 if (!$sid) {
255 return "";
257 if(!$dbh) {
258 $dbh = db_connect();
260 $q = "SELECT Users.ID ";
261 $q.= "FROM Users, Sessions ";
262 $q.= "WHERE Users.ID = Sessions.UsersID ";
263 $q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
264 $result = $dbh->query($q);
265 if (!$result) {
266 return 0;
268 $row = $result->fetch(PDO::FETCH_NUM);
270 return $row[0];
274 * Establish a connection with a database using PDO
276 * @return \PDO A database connection
278 function db_connect() {
279 try {
280 $dbh = new PDO(AUR_db_DSN_prefix . ":" . AUR_db_host . ";dbname=" . AUR_db_name, AUR_db_user, AUR_db_pass);
282 catch (PDOException $e) {
283 echo "Error - Could not connect to AUR database: " . $e->getMessage();
286 $dbh->exec("SET NAMES 'utf8' COLLATE 'utf8_general_ci';");
288 return $dbh;
292 * Common AUR header displayed on all pages
294 * @global string $LANG Language selected by the visitor
295 * @global array $SUPPORTED_LANGS Languages that are supported by the AUR
296 * @param string $title Name of the AUR page to be displayed on browser
298 * @return void
300 function html_header($title="") {
301 global $AUR_LOCATION;
302 global $DISABLE_HTTP_LOGIN;
303 global $LANG;
304 global $SUPPORTED_LANGS;
306 include('header.php');
307 return;
311 * Common AUR footer displayed on all pages
313 * @param string $ver The AUR version
315 * @return void
317 function html_footer($ver="") {
318 include('footer.php');
319 return;
323 * Determine if a user has permission to submit a package
325 * @param string $name Name of the package to be submitted
326 * @param string $sid User's session ID
327 * @param \PDO $dbh Already established database connection
329 * @return int 0 if the user can't submit, 1 if the user can submit
331 function can_submit_pkg($name="", $sid="", $dbh=NULL) {
332 if (!$name || !$sid) {return 0;}
333 if(!$dbh) {
334 $dbh = db_connect();
336 $q = "SELECT MaintainerUID ";
337 $q.= "FROM Packages WHERE Name = " . $dbh->quote($name);
338 $result = $dbh->query($q);
339 $row = $result->fetch(PDO::FETCH_NUM);
341 if (!$row[0]) {
342 return 1;
344 $my_uid = uid_from_sid($sid, $dbh);
346 if ($row[0] === NULL || $row[0] == $my_uid) {
347 return 1;
350 return 0;
354 * Recursively delete a directory
356 * @param string $dirname Name of the directory to be removed
358 * @return void
360 function rm_tree($dirname) {
361 if (empty($dirname) || !is_dir($dirname)) return;
363 foreach (scandir($dirname) as $item) {
364 if ($item != '.' && $item != '..') {
365 $path = $dirname . '/' . $item;
366 if (is_file($path) || is_link($path)) {
367 unlink($path);
369 else {
370 rm_tree($path);
375 rmdir($dirname);
377 return;
381 * Determine the user's ID in the database using a username
383 * @param string $username The username of an account
384 * @param \PDO $dbh Already established database connection
386 * @return string Return user ID if exists for username, otherwise "None"
388 function uid_from_username($username="", $dbh=NULL) {
389 if (!$username) {
390 return "";
392 if(!$dbh) {
393 $dbh = db_connect();
395 $q = "SELECT ID FROM Users WHERE Username = " . $dbh->quote($username);
396 $result = $dbh->query($q);
397 if (!$result) {
398 return "None";
400 $row = $result->fetch(PDO::FETCH_NUM);
402 return $row[0];
406 * Determine the user's ID in the database using an e-mail address
408 * @param string $email An e-mail address in foo@example.com format
409 * @param \PDO $dbh Already established database connection
411 * @return string The user's ID
413 function uid_from_email($email="", $dbh=NULL) {
414 if (!$email) {
415 return "";
417 if(!$dbh) {
418 $dbh = db_connect();
420 $q = "SELECT ID FROM Users WHERE Email = " . $dbh->quote($email);
421 $result = $dbh->query($q);
422 if (!$result) {
423 return "None";
425 $row = $result->fetch(PDO::FETCH_NUM);
427 return $row[0];
431 * Determine if a user has TU or Developer privileges
433 * @return bool Return true if the user is a TU or developer, otherwise false
435 function check_user_privileges() {
436 $type = account_from_sid($_COOKIE['AURSID']);
437 return ($type == 'Trusted User' || $type == 'Developer');
441 * Generate clean url with edited/added user values
443 * Makes a clean string of variables for use in URLs based on current $_GET and
444 * 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")
448 * @param string $append string of variables and values formatted as in URLs
450 * @return string clean string of variables to append to URL, urlencoded
452 function mkurl($append) {
453 $get = $_GET;
454 $append = explode('&', $append);
455 $uservars = array();
456 $out = '';
458 foreach ($append as $i) {
459 $ex = explode('=', $i);
460 $uservars[$ex[0]] = $ex[1];
463 foreach ($uservars as $k => $v) { $get[$k] = $v; }
465 foreach ($get as $k => $v) {
466 if ($v !== '') {
467 $out .= '&amp;' . urlencode($k) . '=' . urlencode($v);
471 return substr($out, 5);
475 * Determine a user's salt from the database
477 * @param string $user_id The user ID of the user trying to log in
478 * @param \PDO $dbh Already established database connection
480 * @return string|void Return the salt for the requested user, otherwise void
482 function get_salt($user_id, $dbh=NULL) {
483 if(!$dbh) {
484 $dbh = db_connect();
486 $q = "SELECT Salt FROM Users WHERE ID = " . $user_id;
487 $result = $dbh->query($q);
488 if ($result) {
489 $row = $result->fetch(PDO::FETCH_NUM);
490 return $row[0];
492 return;
496 * Save a user's salted password in the database
498 * @param string $user_id The user ID of the user who is salting their password
499 * @param string $passwd The password of the user logging in
500 * @param \PDO $dbh Already established database connection
502 function save_salt($user_id, $passwd, $dbh=NULL) {
503 if(!$dbh) {
504 $dbh = db_connect();
506 $salt = generate_salt();
507 $hash = salted_hash($passwd, $salt);
508 $q = "UPDATE Users SET Salt = " . $dbh->quote($salt) . ", ";
509 $q.= "Passwd = " . $dbh->quote($hash) . " WHERE ID = " . $user_id;
510 $result = $dbh->exec($q);
514 * Generate a string to be used for salting passwords
516 * @return string MD5 hash of concatenated random number and current time
518 function generate_salt() {
519 return md5(uniqid(mt_rand(), true));
523 * Combine salt and password to form a hash
525 * @param string $passwd User plaintext password
526 * @param string $salt MD5 hash to be used as user salt
528 * @return string The MD5 hash of the concatenated salt and user password
530 function salted_hash($passwd, $salt) {
531 if (strlen($salt) != 32) {
532 trigger_error('Salt does not look like an md5 hash', E_USER_WARNING);
534 return md5($salt . $passwd);
538 * Process submitted comments so any links can be followed
540 * @param string $comment Raw user submitted package comment
542 * @return string User comment with links printed in HTML
544 function parse_comment($comment) {
545 $url_pattern = '/(\b(?:https?|ftp):\/\/[\w\/\#~:.?+=&%@!\-;,]+?' .
546 '(?=[.:?\-;,]*(?:[^\w\/\#~:.?+=&%@!\-;,]|$)))/iS';
548 $matches = preg_split($url_pattern, $comment, -1,
549 PREG_SPLIT_DELIM_CAPTURE);
551 $html = '';
552 for ($i = 0; $i < count($matches); $i++) {
553 if ($i % 2) {
554 # convert links
555 $html .= '<a href="' . htmlspecialchars($matches[$i]) .
556 '">' . htmlspecialchars($matches[$i]) . '</a>';
558 else {
559 # convert everything else
560 $html .= nl2br(htmlspecialchars($matches[$i]));
564 return $html;
568 * Wrapper for beginning a database transaction
570 * @param \PDO $dbh Already established database connection
572 function begin_atomic_commit($dbh=NULL) {
573 if(!$dbh) {
574 $dbh = db_connect();
576 $dbh->beginTransaction();
580 * Wrapper for committing a database transaction
582 * @param \PDO $dbh Already established database connection
584 function end_atomic_commit($dbh=NULL) {
585 if(!$dbh) {
586 $dbh = db_connect();
588 $dbh->commit();
593 * Determine the row ID for the most recently insterted row
595 * @param \PDO $dbh Already established database connection
597 * @return string The ID of the last inserted row
599 function last_insert_id($dbh=NULL) {
600 if(!$dbh) {
601 $dbh = db_connect();
603 return $dbh->lastInsertId();
607 * Determine package information for latest package
609 * @param int $numpkgs Number of packages to get information on
610 * @param \PDO $dbh Already established database connection
612 * @return array $packages Package info for the specified number of recent packages
614 function latest_pkgs($numpkgs, $dbh=NULL) {
615 if(!$dbh) {
616 $dbh = db_connect();
619 $q = "SELECT * FROM Packages ";
620 $q.= "ORDER BY SubmittedTS DESC ";
621 $q.= "LIMIT " .intval($numpkgs);
622 $result = $dbh->query($q);
624 if ($result) {
625 while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
626 $packages[] = $row;
630 return $packages;