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');
7 header('X-Frame-Options: DENY');
9 date_default_timezone_set('UTC');
11 include_once('translator.inc.php');
14 include_once("DB.class.php");
15 include_once("routing.inc.php");
16 include_once("version.inc.php");
17 include_once("acctfuncs.inc.php");
18 include_once("cachefuncs.inc.php");
19 include_once("confparser.inc.php");
20 include_once("credentials.inc.php");
22 include_once('timezone.inc.php');
29 * Check if a visitor is logged in
31 * Query "Sessions" table with supplied cookie. Determine if the cookie is valid
32 * or not. Unset the cookie if invalid or session timeout reached. Update the
33 * session timeout if it is still valid.
35 * @global array $_COOKIE User cookie values
39 function check_sid() {
42 if (isset($_COOKIE["AURSID"])) {
44 $timeout = config_get_int('options', 'login_timeout');
45 # the visitor is logged in, try and update the session
48 $q = "SELECT LastUpdateTS, " . strval(time()) . " FROM Sessions ";
49 $q.= "WHERE SessionID = " . $dbh->quote($_COOKIE["AURSID"]);
50 $result = $dbh->query($q);
51 $row = $result->fetch(PDO
::FETCH_NUM
);
54 # Invalid SessionID - hacker alert!
58 $last_update = $row[0];
59 if ($last_update +
$timeout <= $row[1]) {
65 # 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!?
68 setcookie("AURSID", "", 1, "/", null, !empty($_SERVER['HTTPS']), true);
69 unset($_COOKIE['AURSID']);
70 } elseif ($failed == 2) {
71 # session id timeout was reached and they must login again.
73 delete_session_id($_COOKIE["AURSID"]);
75 setcookie("AURSID", "", 1, "/", null, !empty($_SERVER['HTTPS']), true);
76 unset($_COOKIE['AURSID']);
78 # still logged in and haven't reached the timeout, go ahead
79 # and update the idle timestamp
81 # Only update the timestamp if it is less than the
82 # current time plus $timeout.
84 # This keeps 'remembered' sessions from being
86 if ($last_update < time() +
$timeout) {
87 $q = "UPDATE Sessions SET LastUpdateTS = " . strval(time()) . " ";
88 $q.= "WHERE SessionID = " . $dbh->quote($_COOKIE["AURSID"]);
97 * Redirect user to the Terms of Service agreement if there are updated terms.
101 function check_tos() {
102 if (!isset($_COOKIE["AURSID"])) {
106 $path = $_SERVER['PATH_INFO'];
107 $route = get_route($path);
108 if (!$route ||
$route == "tos.php") {
112 if (count(fetch_updated_terms(uid_from_sid($_COOKIE["AURSID"]))) > 0) {
113 header('Location: ' . get_uri('/tos'));
119 * Verify the supplied CSRF token matches expected token
121 * @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 return ($_POST['token'] == $_COOKIE['AURSID']);
132 * Verify a user supplied e-mail against RFC 3696 and DNS records
134 * @param string $addy E-mail address being validated in foo@example.com format
136 * @return bool True if e-mail passes validity checks, otherwise false
138 function valid_email($addy) {
139 // check against RFC 3696
140 if (filter_var($addy, FILTER_VALIDATE_EMAIL
) === false) {
144 // check dns for mx, a, aaaa records
145 list($local, $domain) = explode('@', $addy);
146 if (!(checkdnsrr($domain, 'MX') ||
checkdnsrr($domain, 'A') ||
checkdnsrr($domain, 'AAAA'))) {
154 * Verify that a given URL is valid and uses the HTTP(s) protocol
156 * @param string $url URL of the home page to be validated
158 * @return bool True if URL passes validity checks, false otherwise
160 function valid_homepage($url) {
161 if (filter_var($url, FILTER_VALIDATE_URL
) === false) {
165 $url_components = parse_url($url);
166 if (!in_array($url_components['scheme'], array('http', 'https'))) {
174 * Generate a unique session ID
176 * @return string MD5 hash of the concatenated user IP, random number, and current time
179 return md5($_SERVER['REMOTE_ADDR'] . uniqid(mt_rand(), true));
183 * Determine the user's username in the database using a user ID
185 * @param string $id User's ID
187 * @return string Username if it exists, otherwise null
189 function username_from_id($id) {
192 $dbh = DB
::connect();
193 $q = "SELECT Username FROM Users WHERE ID = " . $dbh->quote($id);
194 $result = $dbh->query($q);
199 $row = $result->fetch(PDO
::FETCH_NUM
);
206 * Determine the user's username in the database using a session ID
208 * @param string $sid User's session ID
210 * @return string Username of the visitor
212 function username_from_sid($sid="") {
216 $dbh = DB
::connect();
217 $q = "SELECT Username ";
218 $q.= "FROM Users, Sessions ";
219 $q.= "WHERE Users.ID = Sessions.UsersID ";
220 $q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
221 $result = $dbh->query($q);
225 $row = $result->fetch(PDO
::FETCH_NUM
);
233 * Format a user name for inclusion in HTML data
235 * @param string $username The user name to format
237 * @return string The generated HTML code for the account link
239 function html_format_username($username) {
240 $username_fmt = $username ?
htmlspecialchars($username, ENT_QUOTES
) : __("None");
242 if ($username && isset($_COOKIE["AURSID"])) {
243 $link = '<a href="' . get_uri('/account/') . $username_fmt;
244 $link .= '" title="' . __('View account information for %s', $username_fmt);
245 $link .= '">' . $username_fmt . '</a>';
248 return $username_fmt;
253 * Format the maintainer and co-maintainers for inclusion in HTML data
255 * @param string $maintainer The user name of the maintainer
256 * @param array $comaintainers The list of co-maintainer user names
258 * @return string The generated HTML code for the account links
260 function html_format_maintainers($maintainer, $comaintainers) {
261 $code = html_format_username($maintainer);
263 if (count($comaintainers) > 0) {
265 foreach ($comaintainers as $comaintainer) {
266 $code .= html_format_username($comaintainer);
267 if ($comaintainer !== end($comaintainers)) {
278 * Format a link in the package actions box
280 * @param string $uri The link target
281 * @param string $inner The HTML code to use for the link label
283 * @return string The generated HTML code for the action link
285 function html_action_link($uri, $inner) {
286 if (isset($_COOKIE["AURSID"])) {
287 $code = '<a href="' . htmlspecialchars($uri, ENT_QUOTES
) . '">';
289 $code = '<a href="' . get_uri('/login/', true) . '?referer=';
290 $code .= urlencode(rtrim(aur_location(), '/') . $uri) . '">';
292 $code .= $inner . '</a>';
298 * Format a form in the package actions box
300 * @param string $uri The link target
301 * @param string $action The action name (passed as HTTP POST parameter)
302 * @param string $inner The HTML code to use for the link label
304 * @return string The generated HTML code for the action link
306 function html_action_form($uri, $action, $inner) {
307 if (isset($_COOKIE["AURSID"])) {
308 $code = '<form action="' . htmlspecialchars($uri, ENT_QUOTES
) . '" ';
309 $code .= 'method="post">';
310 $code .= '<input type="hidden" name="token" value="';
311 $code .= htmlspecialchars($_COOKIE['AURSID'], ENT_QUOTES
) . '" />';
312 $code .= '<input type="submit" class="button text-button" name="';
313 $code .= htmlspecialchars($action, ENT_QUOTES
) . '" ';
314 $code .= 'value="' . $inner . '" />';
317 $code = '<a href="' . get_uri('/login/', true) . '">';
318 $code .= $inner . '</a>';
325 * Determine the user's e-mail address in the database using a session ID
327 * @param string $sid User's session ID
329 * @return string User's e-mail address as given during registration
331 function email_from_sid($sid="") {
335 $dbh = DB
::connect();
336 $q = "SELECT Email ";
337 $q.= "FROM Users, Sessions ";
338 $q.= "WHERE Users.ID = Sessions.UsersID ";
339 $q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
340 $result = $dbh->query($q);
344 $row = $result->fetch(PDO
::FETCH_NUM
);
352 * Determine the user's account type in the database using a session ID
354 * @param string $sid User's session ID
356 * @return string Account type of user ("User", "Trusted User", or "Developer")
358 function account_from_sid($sid="") {
362 $dbh = DB
::connect();
363 $q = "SELECT AccountType ";
364 $q.= "FROM Users, AccountTypes, Sessions ";
365 $q.= "WHERE Users.ID = Sessions.UsersID ";
366 $q.= "AND AccountTypes.ID = Users.AccountTypeID ";
367 $q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
368 $result = $dbh->query($q);
372 $row = $result->fetch(PDO
::FETCH_NUM
);
380 * Determine the user's ID in the database using a session ID
382 * @param string $sid User's session ID
384 * @return string|int The user's name, 0 on query failure
386 function uid_from_sid($sid="") {
390 $dbh = DB
::connect();
391 $q = "SELECT Users.ID ";
392 $q.= "FROM Users, Sessions ";
393 $q.= "WHERE Users.ID = Sessions.UsersID ";
394 $q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
395 $result = $dbh->query($q);
399 $row = $result->fetch(PDO
::FETCH_NUM
);
407 * Common AUR header displayed on all pages
409 * @global string $LANG Language selected by the visitor
410 * @global array $SUPPORTED_LANGS Languages that are supported by the AUR
411 * @param string $title Name of the AUR page to be displayed on browser
415 function html_header($title="", $details=array()) {
417 global $SUPPORTED_LANGS;
419 include('header.php');
424 * Common AUR footer displayed on all pages
426 * @param string $ver The AUR version
430 function html_footer($ver="") {
431 include('footer.php');
436 * Determine if a user has permission to submit a package
438 * @param string $name Name of the package to be submitted
439 * @param string $sid User's session ID
441 * @return int 0 if the user can't submit, 1 if the user can submit
443 function can_submit_pkgbase($name="", $sid="") {
444 if (!$name ||
!$sid) {return 0;}
445 $dbh = DB
::connect();
446 $q = "SELECT MaintainerUID ";
447 $q.= "FROM PackageBases WHERE Name = " . $dbh->quote($name);
448 $result = $dbh->query($q);
449 $row = $result->fetch(PDO
::FETCH_NUM
);
454 $my_uid = uid_from_sid($sid);
456 if ($row[0] === NULL ||
$row[0] == $my_uid) {
464 * Determine if a package can be overwritten by some package base
466 * @param string $name Name of the package to be submitted
467 * @param int $base_id The ID of the package base
469 * @return bool True if the package can be overwritten, false if not
471 function can_submit_pkg($name, $base_id) {
472 $dbh = DB
::connect();
473 $q = "SELECT COUNT(*) FROM Packages WHERE ";
474 $q.= "Name = " . $dbh->quote($name) . " AND ";
475 $q.= "PackageBaseID <> " . intval($base_id);
476 $result = $dbh->query($q);
478 if (!$result) return false;
479 return ($result->fetchColumn() == 0);
483 * Recursively delete a directory
485 * @param string $dirname Name of the directory to be removed
489 function rm_tree($dirname) {
490 if (empty($dirname) ||
!is_dir($dirname)) return;
492 foreach (scandir($dirname) as $item) {
493 if ($item != '.' && $item != '..') {
494 $path = $dirname . '/' . $item;
495 if (is_file($path) ||
is_link($path)) {
510 * Determine the user's ID in the database using a username
512 * @param string $username The username of an account
514 * @return string Return user ID if exists for username, otherwise null
516 function uid_from_username($username) {
517 $dbh = DB
::connect();
518 $q = "SELECT ID FROM Users WHERE Username = " . $dbh->quote($username);
519 $result = $dbh->query($q);
524 $row = $result->fetch(PDO
::FETCH_NUM
);
531 * Determine the user's ID in the database using a username or email address
533 * @param string $username The username or email address of an account
535 * @return string Return user ID if exists, otherwise null
537 function uid_from_loginname($loginname) {
538 $uid = uid_from_username($loginname);
540 $uid = uid_from_email($loginname);
546 * Determine the user's ID in the database using an e-mail address
548 * @param string $email An e-mail address in foo@example.com format
550 * @return string The user's ID
552 function uid_from_email($email) {
553 $dbh = DB
::connect();
554 $q = "SELECT ID FROM Users WHERE Email = " . $dbh->quote($email);
555 $result = $dbh->query($q);
560 $row = $result->fetch(PDO
::FETCH_NUM
);
567 * Generate clean url with edited/added user values
569 * Makes a clean string of variables for use in URLs based on current $_GET and
570 * 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")
574 * @param string $append string of variables and values formatted as in URLs
576 * @return string clean string of variables to append to URL, urlencoded
578 function mkurl($append) {
580 $append = explode('&', $append);
584 foreach ($append as $i) {
585 $ex = explode('=', $i);
586 $uservars[$ex[0]] = $ex[1];
589 foreach ($uservars as $k => $v) { $get[$k] = $v; }
591 foreach ($get as $k => $v) {
593 $out .= '&' . urlencode($k) . '=' . urlencode($v);
597 return substr($out, 5);
601 * Get a package comment
603 * @param int $comment_id The ID of the comment
605 * @return array The user ID and comment OR null, null in case of an error
607 function comment_by_id($comment_id) {
608 $dbh = DB
::connect();
609 $q = "SELECT UsersID, Comments FROM PackageComments ";
610 $q.= "WHERE ID = " . intval($comment_id);
611 $result = $dbh->query($q);
613 return array(null, null);
616 return $result->fetch(PDO
::FETCH_NUM
);
620 * Process submitted comments so any links can be followed
622 * @param string $comment Raw user submitted package comment
624 * @return string User comment with links printed in HTML
626 function parse_comment($comment) {
627 $url_pattern = '/(\b(?:https?|ftp):\/\/[\w\/\#~:.?+=&%@!\-;,]+?' .
628 '(?=[.:?\-;,]*(?:[^\w\/\#~:.?+=&%@!\-;,]|$)))/iS';
630 $matches = preg_split($url_pattern, $comment, -1,
631 PREG_SPLIT_DELIM_CAPTURE
);
634 for ($i = 0; $i < count($matches); $i++
) {
637 $html .= '<a href="' . htmlspecialchars($matches[$i]) .
638 '" rel="nofollow">' . htmlspecialchars($matches[$i]) . '</a>';
641 # convert everything else
642 $html .= nl2br(htmlspecialchars($matches[$i]));
650 * Wrapper for beginning a database transaction
652 function begin_atomic_commit() {
653 $dbh = DB
::connect();
654 $dbh->beginTransaction();
658 * Wrapper for committing a database transaction
660 function end_atomic_commit() {
661 $dbh = DB
::connect();
666 * Merge pkgbase and package options
668 * Merges entries of the first and the second array. If any key appears in both
669 * arrays and the corresponding value in the second array is either a non-array
670 * type or a non-empty array, the value from the second array replaces the
671 * value from the first array. If the value from the second array is an array
672 * containing a single empty string, the value in the resulting array becomes
673 * an empty array instead. If the value in the second array is empty, the
674 * resulting array contains the value from the first array.
676 * @param array $pkgbase_info Options from the pkgbase section
677 * @param array $section_info Options from the package section
679 * @return array Merged information from both sections
681 function array_pkgbuild_merge($pkgbase_info, $section_info) {
683 foreach ($section_info as $opt_key => $opt_val) {
684 if (is_array($opt_val)) {
685 if ($opt_val == array('')) {
686 $pi[$opt_key] = array();
687 } elseif (count($opt_val) > 0) {
688 $pi[$opt_key] = $opt_val;
691 $pi[$opt_key] = $opt_val;
698 * Bound an integer value between two values
700 * @param int $n Integer value to bound
701 * @param int $min Lower bound
702 * @param int $max Upper bound
704 * @return int Bounded integer value
706 function bound($n, $min, $max) {
707 return min(max($n, $min), $max);
711 * Return the URL of the AUR root
713 * @return string The URL of the AUR root
715 function aur_location() {
716 $location = config_get('options', 'aur_location');
717 if (substr($location, -1) != '/') {
724 * Calculate pagination templates
726 * @return array The array of pagination templates, per page, and offset values
728 function calculate_pagination($total_comment_count) {
729 /* Sanitize paging variables. */
730 if (isset($_GET["O"])) {
731 $_GET["O"] = max(intval($_GET["O"]), 0);
735 $offset = $_GET["O"];
737 if (isset($_GET["PP"])) {
738 $_GET["PP"] = bound(intval($_GET["PP"]), 1, 250);
742 $per_page = $_GET["PP"];
744 // Page offsets start at zero, so page 2 has offset 1, which means that we
745 // need to add 1 to the offset to get the current page.
746 $current_page = ceil($offset / $per_page) +
1;
747 $num_pages = ceil($total_comment_count / $per_page);
748 $pagination_templs = array();
750 if ($current_page > 1) {
751 $previous_page = $current_page - 1;
752 $previous_offset = ($previous_page - 1) * $per_page;
753 $pagination_templs['« ' . __('First')] = 0;
754 $pagination_templs['‹ ' . __('Previous')] = $previous_offset;
757 if ($current_page - 5 > 1) {
758 $pagination_templs["..."] = false;
761 for ($i = max($current_page - 5, 1); $i <= min($num_pages, $current_page +
5); $i++
) {
762 $pagination_templs[$i] = ($i - 1) * $per_page;
765 if ($current_page +
5 < $num_pages)
766 $pagination_templs["... "] = false;
768 if ($current_page < $num_pages) {
769 $pagination_templs[__('Next') . ' ›'] = $current_page * $per_page;
770 $pagination_templs[__('Last') . ' »'] = ($num_pages - 1) * $per_page;
773 return array($pagination_templs, $per_page, $offset);