Commit 76343fb9 authored by Lukas Fleischer's avatar Lukas Fleischer
Browse files

Use an INI-style configuration file



Replace web/lib/config.inc.php with an INI-style configuration file.
This allows us to get rid of several globals and makes it easier to use
the same configuration file in external scripts.
Signed-off-by: default avatarLukas Fleischer <archlinux@cryptocrack.de>
parent a0a52307
[database]
dsn_prefix = mysql
host = localhost
socket = /var/run/mysqld/mysqld.sock
name = AUR
user = aur
password = aur
[paths]
storage = /srv/aur/unsupported/
[options]
username_min_len = 3
username_max_len = 16
passwd_min_len = 4
default_lang = en
sql_debug = 0
max_sessions_per_user = 8
login_timeout = 7200
persistent_cookie_timeout = 2592000
max_filesize_uncompressed = 8388608
disable_http_login = 1
aur_location = http://localhost
package_url = /packages/
use_virtual_urls = 1
max_rpc_results = 5000
aur_request_ml = aur-requests@archlinux.org
request_idle_time = 1209600
auto_orphan_age = 15552000
......@@ -8,10 +8,14 @@ if (empty($dir)) {
}
set_include_path(get_include_path() . PATH_SEPARATOR . "$dir/lib");
include("config.inc.php");
include("confparser.inc.php");
$user = config_get('database', 'user');
$password = config_get('database', 'password');
$name = config_get('database', 'name');
exec($dir . "/../scripts/aurblup/aurblup " .
"-S /var/run/mysqld/mysqld.sock " .
"-u " . escapeshellarg(AUR_db_user) . " " .
"-p " . escapeshellarg(AUR_db_pass) . " " .
"-D " . escapeshellarg(AUR_db_name));
"-u " . escapeshellarg($user) . " " .
"-p " . escapeshellarg($password) . " " .
"-D " . escapeshellarg($name));
......@@ -16,24 +16,25 @@ if (empty($dir)) {
}
set_include_path(get_include_path() . PATH_SEPARATOR . "$dir/lib");
include("config.inc.php");
include("confparser.inc.php");
include("aur.inc.php");
include("pkgfuncs.inc.php");
$count = 0;
$buckets = scandir(INCOMING_DIR);
$incoming_dir = config_get('paths', 'storage');
$buckets = scandir($incoming_dir);
foreach ($buckets as $bucket) {
$bucketpath = INCOMING_DIR . $bucket;
$bucketpath = $incoming_dir . $bucket;
if ($bucket == '.' || $bucket == '..' || !is_dir($bucketpath)) {
continue;
}
$files = scandir(INCOMING_DIR . $bucket);
$files = scandir($incoming_dir . $bucket);
foreach ($files as $pkgname) {
if ($pkgname == '.' || $pkgname == '..') {
continue;
}
$fullpath = INCOMING_DIR . $bucket . "/" . $pkgname;
$fullpath = $incoming_dir . $bucket . "/" . $pkgname;
if (!pkg_from_name($pkgname) && is_dir($fullpath)) {
echo 'Removing ' . $fullpath . "\n";
rm_tree($fullpath);
......
......@@ -17,3 +17,7 @@ ALTER TABLE PackageVotes ADD COLUMN VoteTS BIGINT NULL DEFAULT NULL;
----
INSERT INTO PackageCategories (Category) VALUES ('wayland');
----
4. The configuration file format has been changed. Make sure you convert
web/lib/config.inc.php to the new format (see conf/config.proto for an example
configuration) and put the resulting file in conf/config.
......@@ -5,7 +5,8 @@ include_once("aur.inc.php");
set_lang();
check_sid();
if (!$DISABLE_HTTP_LOGIN || (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'])) {
$disable_http_login = config_get_bool('options', 'disable_http_login');
if (!$disable_http_login || (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'])) {
$login = try_login();
$login_error = $login['error'];
}
......@@ -19,7 +20,7 @@ html_header('AUR ' . __("Login"));
<?= __("Logged-in as: %s", '<strong>' . username_from_sid($_COOKIE["AURSID"]) . '</strong>'); ?>
<a href="<?= get_uri('/logout/'); ?>">[<?= __("Logout"); ?>]</a>
</p>
<?php elseif (!$DISABLE_HTTP_LOGIN || (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'])): ?>
<?php elseif (!$disable_http_login || (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'])): ?>
<form method="post" action="<?= get_uri('/login') ?>">
<fieldset>
<legend><?= __('Enter login credentials') ?></legend>
......@@ -28,7 +29,7 @@ html_header('AUR ' . __("Login"));
<?php endif; ?>
<p>
<label for="id_username"><?= __('Username') . ':'; ?></label>
<input id="id_username" type="text" name="user" size="30" maxlength="<?= USERNAME_MAX_LEN; ?>" value="<?php if (isset($_POST['user'])) { print htmlspecialchars($_POST['user'], ENT_QUOTES); } ?>" />
<input id="id_username" type="text" name="user" size="30" maxlength="<?= config_get_int('options', 'username_max_len'); ?>" value="<?php if (isset($_POST['user'])) { print htmlspecialchars($_POST['user'], ENT_QUOTES); } ?>" />
</p>
<p>
<label for="id_password"><?= __('Password') . ':'; ?></label>
......@@ -47,7 +48,7 @@ html_header('AUR ' . __("Login"));
<?php else: ?>
<p>
<?php printf(__("HTTP login is disabled. Please %sswitch to HTTPs%s if you want to login."),
'<a href="' . $AUR_LOCATION . get_uri('/login') . '">', '</a>'); ?>
'<a href="' . aur_location() . get_uri('/login') . '">', '</a>'); ?>
</p>
<?php endif; ?>
</div>
......
<?php
set_include_path(get_include_path() . PATH_SEPARATOR . '../lib');
include_once("config.inc.php");
require_once('Archive/Tar.php');
......@@ -64,11 +63,12 @@ if ($uid):
}
# Check uncompressed file size (ZIP bomb protection)
if (!$error && $MAX_FILESIZE_UNCOMPRESSED) {
$max_filesize_uncompressed = config_get_int('options', 'max_filesize_uncompressed');
if (!$error && $max_filesize_uncompressed) {
fseek($fh, -4, SEEK_END);
list(, $filesize_uncompressed) = unpack('V', fread($fh, 4));
if ($filesize_uncompressed > $MAX_FILESIZE_UNCOMPRESSED) {
if ($filesize_uncompressed > $max_filesize_uncompressed) {
$error = __("Error - uncompressed file size too large.");
}
}
......@@ -273,7 +273,7 @@ if ($uid):
}
if (isset($pkgbase_name)) {
$incoming_pkgdir = INCOMING_DIR . substr($pkgbase_name, 0, 2) . "/" . $pkgbase_name;
$incoming_pkgdir = config_get('paths', 'storage') . substr($pkgbase_name, 0, 2) . "/" . $pkgbase_name;
}
/* Upload PKGBUILD and tarball. */
......
<?php
include_once("confparser.inc.php");
class DB {
/**
......@@ -15,8 +17,19 @@ class DB {
public static function connect() {
if (self::$dbh === null) {
try {
self::$dbh = new PDO(AUR_db_DSN_prefix . ":" . AUR_db_host
. ";dbname=" . AUR_db_name, AUR_db_user, AUR_db_pass);
$dsn_prefix = config_get('database', 'dsn_prefix');
$host = config_get('database', 'host');
$socket = config_get('database', 'socket');
$name = config_get('database', 'name');
$user = config_get('database', 'user');
$password = config_get('database', 'password');
$dsn = $dsn_prefix .
':host=' . $host .
';unix_socket=' . $socket .
';dbname=' . $name;
self::$dbh = new PDO($dsn, $user, $password);
self::$dbh->exec("SET NAMES 'utf8' COLLATE 'utf8_general_ci';");
} catch (PDOException $e) {
die('Error - Could not connect to AUR database');
......
......@@ -89,7 +89,7 @@ function display_account_form($A,$U="",$T="",$S="",
*/
function process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",
$P="",$C="",$R="",$L="",$I="",$K="",$J="",$UID=0) {
global $SUPPORTED_LANGS, $AUR_LOCATION;
global $SUPPORTED_LANGS;
$error = '';
......@@ -118,9 +118,11 @@ function process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",
}
if (!$error && !valid_username($U)) {
$length_min = config_get_int('options', 'username_min_len');
$length_max = config_get_int('options', 'username_max_len');
$error = __("The username is invalid.") . "<ul>\n"
."<li>" . __("It must be between %s and %s characters long",
USERNAME_MIN_LEN, USERNAME_MAX_LEN )
. "<li>" . __("It must be between %s and %s characters long", $length_min, $length_max)
. "</li>"
. "<li>" . __("Start and end with a letter or number") . "</li>"
. "<li>" . __("Can contain only one period, underscore or hyphen.")
......@@ -130,8 +132,11 @@ function process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",
if (!$error && $P && $C && ($P != $C)) {
$error = __("Password fields do not match.");
}
if (!$error && $P != '' && !good_passwd($P))
$error = __("Your password must be at least %s characters.",PASSWD_MIN_LEN);
if (!$error && $P != '' && !good_passwd($P)) {
$length_min = config_get_int('options', 'passwd_min_len');
$error = __("Your password must be at least %s characters.",
$length_min);
}
if (!$error && !valid_email($E)) {
$error = __("The email address is invalid.");
......@@ -244,7 +249,7 @@ function process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",
'not work try copying and ' .
'pasting it into your ' .
'browser.',
$AUR_LOCATION);
aur_location());
send_resetkey($email, $subject, $body);
print __("A password reset key has been sent to your e-mail address.");
......@@ -406,14 +411,9 @@ function search_results_page($O=0,$SB="",$U="",$T="",
/**
* Attempt to login and generate a session
*
* @global int $MAX_SESSIONS_PER_USER Maximum sessions a single user may have open
* @global int $PERSISTENT_COOKIE_TIMEOUT Time until cookie expires
*
* @return array Session ID for user, error message if applicable
*/
function try_login() {
global $MAX_SESSIONS_PER_USER, $PERSISTENT_COOKIE_TIMEOUT;
$login_error = "";
$new_sid = "";
$userID = null;
......@@ -456,16 +456,17 @@ function try_login() {
/* Generate a session ID and store it. */
while (!$logged_in && $num_tries < 5) {
if ($MAX_SESSIONS_PER_USER) {
$session_limit = config_get_int('options', 'max_sessions_per_user');
if ($session_limit) {
/*
* Delete all user sessions except the
* last ($MAX_SESSIONS_PER_USER - 1).
* last ($session_limit - 1).
*/
$q = "DELETE s.* FROM Sessions s ";
$q.= "LEFT JOIN (SELECT SessionID FROM Sessions ";
$q.= "WHERE UsersId = " . $userID . " ";
$q.= "ORDER BY LastUpdateTS DESC ";
$q.= "LIMIT " . ($MAX_SESSIONS_PER_USER - 1) . ") q ";
$q.= "LIMIT " . ($session_limit - 1) . ") q ";
$q.= "ON s.SessionID = q.SessionID ";
$q.= "WHERE s.UsersId = " . $userID . " ";
$q.= "AND q.SessionID IS NULL;";
......@@ -499,7 +500,8 @@ function try_login() {
/* Set the SID cookie. */
if (isset($_POST['remember_me']) && $_POST['remember_me'] == "on") {
/* Set cookies for 30 days. */
$cookie_time = time() + $PERSISTENT_COOKIE_TIMEOUT;
$timeout = config_get_int('options', 'persistent_cookie_timeout');
$cookie_time = time() + $timeout;
/* Set session for 30 days. */
$q = "UPDATE Sessions SET LastUpdateTS = $cookie_time ";
......@@ -531,18 +533,20 @@ function is_ipbanned() {
/**
* Validate a username against a collection of rules
*
* The username must be longer or equal to USERNAME_MIN_LEN. It must be shorter
* or equal to USERNAME_MAX_LEN. It must start and end with either a letter or
* a number. It can contain one period, hypen, or underscore. Returns boolean
* of whether name is valid.
* The username must be longer or equal to the configured minimum length. It
* must be shorter or equal to the configured maximum length. It must start and
* end with either a letter or a number. It can contain one period, hypen, or
* underscore. Returns boolean of whether name is valid.
*
* @param string $user Username to validate
*
* @return bool True if username meets criteria, otherwise false
*/
function valid_username($user) {
if (strlen($user) < USERNAME_MIN_LEN ||
strlen($user) > USERNAME_MAX_LEN) {
$length_min = config_get_int('options', 'username_min_len');
$length_max = config_get_int('options', 'username_max_len');
if (strlen($user) < $length_min || strlen($user) > $length_max) {
return false;
} else if (!preg_match("/^[a-z0-9]+[.\-_]?[a-z0-9]+$/Di", $user)) {
return false;
......@@ -645,8 +649,6 @@ function create_resetkey($resetkey, $uid) {
* @return void
*/
function send_resetkey($email, $subject, $body) {
global $AUR_LOCATION;
$uid = uid_from_email($email);
if ($uid == null) {
return;
......@@ -658,9 +660,8 @@ function send_resetkey($email, $subject, $body) {
/* Send e-mail with confirmation link. */
$body = wordwrap($body, 70);
$body .= "\n\n".
"{$AUR_LOCATION}/" . get_uri('/passreset/') . "?".
"resetkey={$resetkey}";
$body .= "\n\n". aur_location() . "/" . get_uri('/passreset/') .
"?resetkey={$resetkey}";
$headers = "MIME-Version: 1.0\r\n" .
"Content-type: text/plain; charset=UTF-8\r\n" .
"Reply-to: noreply@aur.archlinux.org\r\n" .
......@@ -708,10 +709,8 @@ function password_reset($hash, $salt, $resetkey, $email) {
* @return bool True if longer than minimum length, otherwise false
*/
function good_passwd($passwd) {
if ( strlen($passwd) >= PASSWD_MIN_LEN ) {
return true;
}
return false;
$length_min = config_get_int('options', 'passwd_min_len');
return (strlen($passwd) >= $length_min);
}
/**
......@@ -903,16 +902,13 @@ function delete_user_sessions($uid) {
/**
* Remove sessions from the database that have exceed the timeout
*
* @global int $LOGIN_TIMEOUT Time until session expires
*
* @return void
*/
function clear_expired_sessions() {
global $LOGIN_TIMEOUT;
$dbh = DB::connect();
$q = "DELETE FROM Sessions WHERE LastUpdateTS < (UNIX_TIMESTAMP() - $LOGIN_TIMEOUT)";
$timeout = config_get_int('options', 'login_timeout');
$q = "DELETE FROM Sessions WHERE LastUpdateTS < (UNIX_TIMESTAMP() - " . $timeout . ")";
$dbh->query($q);
return;
......
......@@ -10,12 +10,12 @@ date_default_timezone_set('UTC');
include_once('translator.inc.php');
set_lang();
include_once("config.inc.php");
include_once("DB.class.php");
include_once("routing.inc.php");
include_once("version.inc.php");
include_once("acctfuncs.inc.php");
include_once("cachefuncs.inc.php");
include_once("confparser.inc.php");
include_once("credentials.inc.php");
/**
......@@ -26,16 +26,15 @@ include_once("credentials.inc.php");
* session timeout if it is still valid.
*
* @global array $_COOKIE User cookie values
* @global string $LOGIN_TIMEOUT Time until session times out
*
* @return void
*/
function check_sid() {
global $_COOKIE;
global $LOGIN_TIMEOUT;
if (isset($_COOKIE["AURSID"])) {
$failed = 0;
$timeout = config_get_int('options', 'login_timeout');
# the visitor is logged in, try and update the session
#
$dbh = DB::connect();
......@@ -50,7 +49,7 @@ function check_sid() {
$failed = 1;
} else {
$last_update = $row[0];
if ($last_update + $LOGIN_TIMEOUT <= $row[1]) {
if ($last_update + $timeout <= $row[1]) {
$failed = 2;
}
}
......@@ -73,11 +72,11 @@ function check_sid() {
# and update the idle timestamp
# Only update the timestamp if it is less than the
# current time plus $LOGIN_TIMEOUT.
# current time plus $timeout.
#
# This keeps 'remembered' sessions from being
# overwritten.
if ($last_update < time() + $LOGIN_TIMEOUT) {
if ($last_update < time() + $timeout) {
$q = "UPDATE Sessions SET LastUpdateTS = UNIX_TIMESTAMP() ";
$q.= "WHERE SessionID = " . $dbh->quote($_COOKIE["AURSID"]);
$dbh->exec($q);
......@@ -274,8 +273,6 @@ function uid_from_sid($sid="") {
* @return void
*/
function html_header($title="", $details=array()) {
global $AUR_LOCATION;
global $DISABLE_HTTP_LOGIN;
global $LANG;
global $SUPPORTED_LANGS;
......@@ -588,3 +585,16 @@ function array_pkgbuild_merge($pkgbase_info, $section_info) {
function bound($n, $min, $max) {
return min(max($n, $min), $max);
}
/**
* 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;
}
......@@ -192,7 +192,8 @@ class AurJSON {
}
private function process_query($type, $where_condition) {
global $MAX_RPC_RESULTS;
$max_results = config_get_int('options', 'max_rpc_results');
$package_url = config_get('options', 'package_url');
if ($this->version == 1) {
$fields = implode(',', self::$fields_v1);
......@@ -207,7 +208,7 @@ class AurJSON {
"ON Licenses.ID = PackageLicenses.LicenseID " .
"WHERE ${where_condition} " .
"GROUP BY Packages.ID " .
"LIMIT $MAX_RPC_RESULTS";
"LIMIT $max_results";
} elseif ($this->version >= 2) {
$fields = implode(',', self::$fields_v2);
$query = "SELECT {$fields} " .
......@@ -216,7 +217,7 @@ class AurJSON {
"LEFT JOIN Users " .
"ON PackageBases.MaintainerUID = Users.ID " .
"WHERE ${where_condition} " .
"LIMIT $MAX_RPC_RESULTS";
"LIMIT $max_results";
}
$result = $this->dbh->query($query);
......@@ -226,7 +227,7 @@ class AurJSON {
while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
$resultcount++;
$pkgbase_name = $row['PackageBase'];
$row['URLPath'] = URL_DIR . substr($pkgbase_name, 0, 2) . "/" . $pkgbase_name . "/" . $pkgbase_name . ".tar.gz";
$row['URLPath'] = $package_url . substr($pkgbase_name, 0, 2) . "/" . $pkgbase_name . "/" . $pkgbase_name . ".tar.gz";
/*
* Unfortunately, mysql_fetch_assoc() returns
......@@ -254,7 +255,7 @@ class AurJSON {
}
}
if ($resultcount === $MAX_RPC_RESULTS) {
if ($resultcount === $max_results) {
return $this->json_error('Too many package results.');
}
......@@ -303,8 +304,6 @@ class AurJSON {
* @return mixed Returns an array of package matches.
*/
private function search($keyword_string) {
global $MAX_RPC_RESULTS;
if (strlen($keyword_string) < 2) {
return $this->json_error('Query arg too small');
}
......
<?php
# NOTE: modify these variables if your MySQL setup is different
define( "AUR_db_DSN_prefix", "mysql" );
define( "AUR_db_host", "unix_socket=/var/run/mysqld/mysqld.sock" );
define( "AUR_db_name", "AUR" );
define( "AUR_db_user", "aur" );
define( "AUR_db_pass", "aur" );
# Configuration of directories where things live
define( "INCOMING_DIR", "/srv/aur/unsupported/" );
define( "URL_DIR", "/packages/" );
define( "USERNAME_MIN_LEN", 3 );
define( "USERNAME_MAX_LEN", 16 );
define( "PASSWD_MIN_LEN", 4 );
# Default language for displayed messages in the web interface.
define("DEFAULT_LANG", "en");
# Enable debug sql output. This sends each query to error_log. Useful for
# development. Should not be enabled in production. Default to 0 (off).
define("SQL_DEBUG", 0);
# Set cache type. Either "APC", "MEMCACHE", or "NONE". Defaults to NONE.
#define("CACHE_TYPE", "APC");
#define("CACHE_TYPE", "MEMCACHE");
# If using memcache cache_type, list servers. You can separate multiple servers
# with a comma, ex: '127.0.0.1:11211,127.0.0.1:11212'. If undefined, defaults
# to '127.0.0.1:11211'.
#define("MEMCACHE_SERVERS", '127.0.0.1:11211');
# Session limit per user
$MAX_SESSIONS_PER_USER = 8;
# Idle seconds before timeout
$LOGIN_TIMEOUT = 7200;
# Session timeout when using "Remember me" cookies
$PERSISTENT_COOKIE_TIMEOUT = 60 * 60 * 24 * 30;
# Uncompressed file size limit for submitted tarballs (ZIP bomb protection) -
# please ensure "upload_max_filesize" is additionally set to no more than 3M,
# otherwise this check might be easy to bypass (FS#22991 for details)
$MAX_FILESIZE_UNCOMPRESSED = 1024 * 1024 * 8;
# Allow HTTPs logins only
$DISABLE_HTTP_LOGIN = true;
# Web URL used in email links and absolute redirects, no trailing slash
$AUR_LOCATION = "http://localhost";
# Use virtual URLs -- to enable this feature, you also need to tell your web
# server to redirect all requests to "/index.php/$uri".
$USE_VIRTUAL_URLS = true;
# Maximum number of package results to return through an RPC connection.
# Avoid setting this too high and having a PHP too much memory error.
$MAX_RPC_RESULTS = 5000;
# Mailing list to send package request notifications to.
$AUR_REQUEST_ML = "aur-requests@archlinux.org";
# Time to wait until a package request is due.
$REQUEST_IDLE_TIME = 60 * 60 * 24 * 14;
# When an orphan request is filed for a package that has been flagged
# out-of-date for the following number of seconds, it is disowned
# automatically.
$AUTO_ORPHAN_AGE = 60 * 60 * 24 * 180;
<?php
function config_get($section, $key) {
global $AUR_CONFIG;
if (!isset($AUR_CONFIG)) {
$AUR_CONFIG = parse_ini_file("../../conf/config", true);
}
return $AUR_CONFIG[$section][$key];
}
function config_get_int($section, $key) {
return intval(config_get($section, $key));
}
function config_get_bool($section, $key) {
$val = strtolower(config_get($section, $key));
return ($val == 'yes' || $val == 'true' || $val == '1');
}
<?php
include_once("config.inc.php");
define("CRED_ACCOUNT_CHANGE_TYPE", 1);
define("CRED_ACCOUNT_EDIT", 2);
......
<?php
include_once("config.inc.php");
include_once("pkgreqfuncs.inc.php");
/**
......@@ -88,7 +88,6 @@ function pkgbase_comments($base_id, $limit, $include_deleted) {
/**
* Add a comment to a package page and send out appropriate notifications
*
* @global string $AUR_LOCATION The AUR's URL used for notification e-mails
* @param string $base_id The package base ID to add the comment on
* @param string $uid The user ID of the individual who left the comment
* @param string $comment The comment left on a package page
......@@ -96,8 +95,6 @@ function pkgbase_comments($base_id, $limit, $include_deleted) {
* @return void
*/
function pkgbase_add_comment($base_id, $uid, $comment) {
global $AUR_LOCATION;
$dbh = DB::connect();
$q = "INSERT INTO PackageComments ";
......@@ -135,7 +132,7 @@ function pkgbase_add_comment($base_id, $uid, $comment) {
* user who posted the comment was in.
*/
<