diff --git a/.docker/init.sh b/.docker/init.sh index 1ef40d9..73a9d92 100644 --- a/.docker/init.sh +++ b/.docker/init.sh @@ -10,8 +10,13 @@ chown nginx:nginx /run/nginx sed -i "s/^nobody:.*$/nobody:x:1000:50::nobody:\/:\/sbin\/nologin/" /etc/passwd sed -i "s/^nobody:.*$/nobody:x:50:/" /etc/group -sed -i "s/^\$dbuser = .*$/\$dbuser = \"ulogger\";/" /var/www/html/config.php -sed -i "s/^\$dbpass = .*$/\$dbpass = \"${DB_USER_PASS}\";/" /var/www/html/config.php +if [ "$ULOGGER_DB_DRIVER" = "sqlite" ]; then + sed -i "s/^\$dbuser = .*$//" /var/www/html/config.php + sed -i "s/^\$dbpass = .*$//" /var/www/html/config.php +else + sed -i "s/^\$dbuser = .*$/\$dbuser = \"ulogger\";/" /var/www/html/config.php + sed -i "s/^\$dbpass = .*$/\$dbpass = \"${DB_USER_PASS}\";/" /var/www/html/config.php +fi if [ "$ULOGGER_DB_DRIVER" = "pgsql" ]; then export PGDATA=/data diff --git a/.tests/tests/DbTest.php b/.tests/tests/DbTest.php new file mode 100644 index 0000000..558eadf --- /dev/null +++ b/.tests/tests/DbTest.php @@ -0,0 +1,63 @@ +assertEquals($testDbName, uDb::getDbName($dsn)); + } + } + + public function testGetDbNameEmptyNames() { + $testDbName = ""; + $defaultDSNs = [ + "mysql:host=db.example.com;port=3306;dbname=", + "mysql:host=db.example.com;port=3306", + "", + null, + "unsupported:host=localhost;port=5432;dbname=;user=test;password=mypass", + "corrupt", + "pgsql:", + "sqlite", + "sqlite3", + "sqlite:" + ]; + + foreach ($defaultDSNs as $dsn) { + $this->assertEquals($testDbName, uDb::getDbName($dsn)); + } + + } + + public function testGetDbFilename() { + $testFileNames = [ + "C:\\Program Files\\Database.db", + ":memory:", + "/tmp/testdb.db3" + ]; + + foreach ($testFileNames as $fileName) { + $this->assertEquals($fileName, uDb::getDbName("sqlite:$fileName")); + } + } +} + +?> \ No newline at end of file diff --git a/.tests/tests/SetupTest.php b/.tests/tests/SetupTest.php new file mode 100644 index 0000000..88be791 --- /dev/null +++ b/.tests/tests/SetupTest.php @@ -0,0 +1,49 @@ +http->get($this->script); + $this->assertEquals(200, $response->getStatusCode(), "Unexpected status code"); + $body = (string) $response->getBody(); + $this->assertContains("", $body); + } + + public function testSetupPhase() { + $options = [ + "http_errors" => false, + "form_params" => [ "command" => "setup" ] + ]; + $response = $this->http->post($this->script, $options); + $this->assertEquals(200, $response->getStatusCode(), "Unexpected status code"); + $body = (string) $response->getBody(); + $this->assertContains("", $body); + } + + public function testAdduserPhase() { + $options = [ + "http_errors" => false, + "form_params" => [ + "command" => "adduser", + "login" => $this->testUser, + "pass" => $this->testPass, + "pass2" => $this->testPass + ] + ]; + $response = $this->http->post($this->script, $options); + $this->assertEquals(200, $response->getStatusCode(), "Unexpected status code"); + $body = (string) $response->getBody(); + $this->assertContains("", $body); + $this->assertEquals(2, $this->getConnection()->getRowCount("users"), "Wrong row count"); + $expected = [ "id" => 2, "login" => $this->testUser ]; + $actual = $this->getConnection()->createQueryTable("users", "SELECT id, login FROM users WHERE id = 2"); + $this->assertTableContains($expected, $actual, "Wrong actual table data"); + $this->assertTrue(password_verify($this->testPass, $this->pdoGetColumn("SELECT password FROM users WHERE id = 2")), "Wrong actual password hash"); + } + +} + +?> \ No newline at end of file diff --git a/helpers/db.php b/helpers/db.php index adbfc3c..de6392c 100644 --- a/helpers/db.php +++ b/helpers/db.php @@ -101,6 +101,11 @@ return self::$tables[$name]; } + /** + * Returns function name for getting date-time column value as unix timestamp + * @param string $column + * @return string + */ public function unix_timestamp($column) { switch (self::$driver) { default: @@ -116,6 +121,11 @@ } } + /** + * Returns function name for getting date-time column value as 'YYYY-MM-DD hh:mm:ss' + * @param string $column + * @return string + */ public function from_unixtime($column) { switch (self::$driver) { default: @@ -131,10 +141,44 @@ } } + /** + * Set character set + * @param string $charset + */ private function setCharset($charset) { if (self::$driver == "pgsql" || self::$driver == "mysql") { $this->query("SET NAMES '$charset'"); } } + + /** + * Extract database name from DSN + * @param string $dsn + * @return string Empty string if not found + */ + static public function getDbName($dsn) { + $name = ""; + if (strpos($dsn, ":") !== false) { + list($scheme, $dsnWithoutScheme) = explode(":", $dsn, 2); + switch ($scheme) { + case "sqlite": + case "sqlite2": + case "sqlite3": + $pattern = "/(.+)/"; + break; + case "pgsql": + $pattern = "/dbname=([^; ]+)/"; + break; + default: + $pattern = "/dbname=([^;]+)/"; + break; + } + $result = preg_match($pattern, $dsnWithoutScheme, $matches); + if ($result === 1) { + $name = $matches[1]; + } + } + return $name; + } } ?> diff --git a/scripts/setup.php b/scripts/setup.php index b606431..f86246a 100644 --- a/scripts/setup.php +++ b/scripts/setup.php @@ -24,50 +24,52 @@ $enabled = false; /* -------------------------------------------- */ /* no user modifications should be needed below */ -if (version_compare(PHP_VERSION, '5.4.0', '<')) { +if (version_compare(PHP_VERSION, "5.4.0", "<")) { die("Sorry, ulogger will not work with PHP version lower than 5.4 (you have " . PHP_VERSION . ")"); } define("ROOT_DIR", dirname(__DIR__)); -require_once(ROOT_DIR . "/helpers/user.php"); +require_once(ROOT_DIR . "/helpers/db.php"); require_once(ROOT_DIR . "/helpers/config.php"); -require_once(ROOT_DIR . "/helpers/utils.php"); require_once(ROOT_DIR . "/helpers/lang.php"); +require_once(ROOT_DIR . "/helpers/user.php"); +require_once(ROOT_DIR . "/helpers/utils.php"); -$command = uUtils::postString('command'); +$command = uUtils::postString("command"); $lang = (new uLang(uConfig::$lang))->getStrings(); $langSetup = (new uLang(uConfig::$lang))->getSetupStrings(); -$prefix = preg_replace('/[^a-z0-9_]/i', '', uConfig::$dbprefix); +$prefix = preg_replace("/[^a-z0-9_]/i", "", uConfig::$dbprefix); $tPositions = $prefix . "positions"; $tTracks = $prefix . "tracks"; $tUsers = $prefix . "users"; -$dbDriver = null; $messages = []; + switch ($command) { case "setup": $error = false; try { - $options = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ]; - $pdo = new PDO(uConfig::$dbdsn, uConfig::$dbuser, uConfig::$dbpass, $options); - $dbDriver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME); + $pdo = getPdo(); } catch (PDOException $e) { $messages[] = "{$langSetup["dbconnectfailed"]}"; - $messages[] = sprintf($langSetup["serversaid"], "" . $e->getMessage() . ""); + $messages[] = sprintf($langSetup["serversaid"], "" . htmlentities($e->getMessage()) . ""); $messages[] = $langSetup["checkdbsettings"]; break; } try { - $queries = getQueries(); + $queries = getQueries($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)); + $pdo->beginTransaction(); foreach ($queries as $query) { $pdo->query($query); } + $pdo->commit(); } catch (PDOException $e) { + $pdo->rollBack(); $messages[] = "{$langSetup["dbqueryfailed"]}"; - $messages[] = sprintf($langSetup["serversaid"], "" . $e->getMessage() . ""); + $messages[] = sprintf($langSetup["serversaid"], "" . htmlentities($e->getMessage()) . ""); $error = true; } $pdo = null; @@ -85,8 +87,8 @@ switch ($command) { break; case "adduser": - $login = uUtils::postString('login'); - $pass = uUtils::postPass('pass'); + $login = uUtils::postString("login"); + $pass = uUtils::postPass("pass"); if (uUser::add($login, $pass) !== false) { $messages[] = "{$langSetup["congratulations"]}"; @@ -107,7 +109,7 @@ switch ($command) { $messages[] = "
"; break; } - if (!function_exists('password_hash')) { + if (!function_exists("password_hash")) { $messages[] = $langSetup["passfuncwarn"]; $messages[] = $langSetup["passfunchack"]; $messages[] = sprintf($langSetup["lineshouldread"], "
//require_once(ROOT_DIR . \"/helpers/password.php\");
", "
require_once(ROOT_DIR . \"/helpers/password.php\");"); @@ -121,18 +123,7 @@ switch ($command) { $messages[] = "
"; break; } - if (empty(uConfig::$dbdsn) || ($dbDriver != "sqlite" && empty(uConfig::$dbuser))) { - if ($dbDriver == "sqlite") { - $required = "\$dbdsn"; - } else { - $required = "\$dbdsn, \$dbuser, \$dbpass"; - } - $messages[] = sprintf($langSetup["nodbsettings"], $required); - $messages[] = $langSetup["dorestart"]; - $messages[] = "
"; - break; - } - if (ini_get("session.auto_start") == '1') { + if (ini_get("session.auto_start") == "1") { $messages[] = sprintf($langSetup["optionwarn"], "session.auto_start", "0 (off)"); $messages[] = $langSetup["dorestart"]; $messages[] = "
"; @@ -144,14 +135,42 @@ switch ($command) { $messages[] = "
"; break; } - $messages[] = sprintf($langSetup["scriptdesc"], "'$tPositions', '$tTracks', '$tUsers'", "" . getDbname(uConfig::$dbdsn) . ""); + if (empty(uConfig::$dbdsn)) { + $messages[] = sprintf($langSetup["nodbsettings"], "\$dbdsn"); + $messages[] = $langSetup["dorestart"]; + $messages[] = "
"; + break; + } + try { + $pdo = getPdo(); + } catch (PDOException $e) { + $isSqlite = stripos(uConfig::$dbdsn, "sqlite") === 0; + if (!$isSqlite && empty(uConfig::$dbuser)) { + $messages[] = sprintf($langSetup["nodbsettings"], "\$dbuser, \$dbpass"); + } else { + $messages[] = $langSetup["dbconnectfailed"]; + $messages[] = $langSetup["checkdbsettings"]; + $messages[] = sprintf($langSetup["serversaid"], "" . htmlentities($e->getMessage()) . ""); + } + $messages[] = $langSetup["dorestart"]; + $messages[] = "
"; + break; + } + $pdo = null; + $dbName = uDb::getDbName(uConfig::$dbdsn); + $dbName = empty($dbName) ? '""' : "" . htmlentities($dbName) . ""; + $messages[] = sprintf($langSetup["scriptdesc"], "'$tPositions', '$tTracks', '$tUsers'", $dbName); $messages[] = $langSetup["scriptdesc2"]; $messages[] = "
"; break; } -function getQueries() { - global $tPositions, $tUsers, $tTracks, $dbDriver; +/** + * @param string $dbDriver + * @return array + */ +function getQueries($dbDriver) { + global $tPositions, $tUsers, $tTracks; $queries = []; switch ($dbDriver) { @@ -284,26 +303,14 @@ function getQueries() { return $queries; } -function getDbname($dsn) { - if (strpos($dsn, ':') !== false) { - list($scheme, $dsnWithoutScheme) = explode(':', $dsn, 2); - switch ($scheme) { - case 'sqlite': - case 'sqlite2': - case 'sqlite3': - return $dsnWithoutScheme; - break; - - default: - $pattern = '~dbname=([^;]*)(?:;|$)~'; - $result = preg_match($pattern, $dsnWithoutScheme, $matches); - if ($result === 1 && !empty($matches[1])) { - return $matches[1]; - } - break; - } - } - return "noname"; +/** + * @return PDO + * @throws PDOException + */ +function getPdo() { + $options = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]; + $pdo = new PDO(uConfig::$dbdsn, uConfig::$dbuser, uConfig::$dbpass, $options); + return $pdo; } ?>