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/.github/stale.yml b/.github/stale.yml
new file mode 100644
index 0000000..dc90e5a
--- /dev/null
+++ b/.github/stale.yml
@@ -0,0 +1,17 @@
+# Number of days of inactivity before an issue becomes stale
+daysUntilStale: 60
+# Number of days of inactivity before a stale issue is closed
+daysUntilClose: 7
+# Issues with these labels will never be considered stale
+exemptLabels:
+ - pinned
+ - security
+# Label to use when marking an issue as stale
+staleLabel: wontfix
+# Comment to post when marking an issue as stale. Set to `false` to disable
+markComment: >
+ This issue has been automatically marked as stale because it has not had
+ recent activity. It will be closed if no further activity occurs. Thank you
+ for your contributions.
+# Comment to post when closing a stale issue. Set to `false` to disable
+closeComment: false
diff --git a/.tests/lib/UloggerAPITestCase.php b/.tests/lib/UloggerAPITestCase.php
index c98334e..45eb67e 100644
--- a/.tests/lib/UloggerAPITestCase.php
+++ b/.tests/lib/UloggerAPITestCase.php
@@ -35,8 +35,8 @@ class UloggerAPITestCase extends BaseDatabaseTestCase {
/**
* Authenticate on server
- * @param string $user Login
- *
+ * @param string|null $user Login (defaults to test user)
+ * @param string|null $pass Optional password (defaults to test password)
* @return bool true on success, false otherwise
*/
public function authenticate($user = NULL, $pass = NULL) {
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/ImportTest.php b/.tests/tests/ImportTest.php
index 0febee2..a59a9f1 100644
--- a/.tests/tests/ImportTest.php
+++ b/.tests/tests/ImportTest.php
@@ -1,5 +1,7 @@
assertEquals(0, $this->getConnection()->getRowCount("positions"), "Wrong row count");
}
-
+ /**
+ * @param ResponseInterface $response
+ * @return bool|SimpleXMLElement
+ */
private function getXMLfromResponse($response) {
$xml = false;
libxml_use_internal_errors(true);
diff --git a/.tests/tests/InternalAPITest.php b/.tests/tests/InternalAPITest.php
index 5de9547..d4472d3 100644
--- a/.tests/tests/InternalAPITest.php
+++ b/.tests/tests/InternalAPITest.php
@@ -1,5 +1,7 @@
assertEquals(1, $this->getConnection()->getRowCount("users"), "Wrong row count");
}
-
+ /**
+ * @param ResponseInterface $response
+ * @return bool|SimpleXMLElement
+ */
private function getXMLfromResponse($response) {
$xml = false;
libxml_use_internal_errors(true);
diff --git a/.tests/tests/PositionTest.php b/.tests/tests/PositionTest.php
index 72bf59f..98a6f0b 100644
--- a/.tests/tests/PositionTest.php
+++ b/.tests/tests/PositionTest.php
@@ -133,7 +133,7 @@ class PositionTest extends UloggerDatabaseTestCase {
$this->assertEquals($trackId2, $position->trackId);
break;
default:
- $this->assert("Unexpected position: {$position->id}");
+ $this->assertTrue(false, "Unexpected position: {$position->id}");
}
}
}
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/composer.json b/composer.json
index 4561df0..292e2b0 100644
--- a/composer.json
+++ b/composer.json
@@ -3,7 +3,9 @@
"ulrichsg/getopt-php": "^3.2",
"ext-json": "*",
"ext-pdo": "*",
- "ext-xmlwriter": "*"
+ "ext-xmlwriter": "*",
+ "ext-simplexml": "*",
+ "ext-libxml": "*"
},
"scripts": {
"test": "./vendor/bin/phpunit"
@@ -13,6 +15,5 @@
"vlucas/phpdotenv": "^3.3",
"guzzlehttp/guzzle": "^6.3",
"phpunit/dbunit": "^2.0"
-
}
}
diff --git a/helpers/auth.php b/helpers/auth.php
index 68920cb..8c345f6 100644
--- a/helpers/auth.php
+++ b/helpers/auth.php
@@ -29,7 +29,7 @@
/** @var bool Is user authenticated */
private $isAuthenticated = false;
- /** @var uUser|null User */
+ /** @var null|uUser */
public $user = null;
public function __construct() {
@@ -107,9 +107,11 @@
}
/**
- * Process log in request
+ * Check valid pass for given login
*
- * @return boolean
+ * @param $login
+ * @param $pass
+ * @return boolean True if valid
*/
public function checkLogin($login, $pass) {
if (!is_null($login) && !is_null($pass)) {
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/helpers/position.php b/helpers/position.php
index 7d57a84..3031c84 100644
--- a/helpers/position.php
+++ b/helpers/position.php
@@ -57,8 +57,6 @@
public $isValid = false;
- private static $db;
-
/**
* Constructor
* @param integer $positionId Position id
diff --git a/helpers/track.php b/helpers/track.php
index 3e7eacc..e2aecf2 100644
--- a/helpers/track.php
+++ b/helpers/track.php
@@ -31,11 +31,6 @@
public $isValid = false;
- /**
- * @var uDb $db
- */
- private static $db = null;
-
/**
* Constructor
*
diff --git a/helpers/user.php b/helpers/user.php
index 7496829..1decc2a 100644
--- a/helpers/user.php
+++ b/helpers/user.php
@@ -34,8 +34,6 @@
public $isAdmin = false;
public $isValid = false;
- private static $db = null;
-
/**
* Constructor
*
diff --git a/scripts/import_cli.php b/scripts/import_cli.php
index cd811af..30ff49d 100644
--- a/scripts/import_cli.php
+++ b/scripts/import_cli.php
@@ -47,7 +47,7 @@ $getopt->addOptions([
Option::create('h', 'help')
->setDescription('Show usage/help'),
- Option::create('u', 'user-id', \GetOpt\GetOpt::OPTIONAL_ARGUMENT)
+ Option::create('u', 'user-id', GetOpt::OPTIONAL_ARGUMENT)
->setDescription('Which user to import the track(s) for (default: 1)')
->setDefaultValue(1)
->setValidation('is_numeric', '%s has to be an integer'),
@@ -60,7 +60,7 @@ $getopt->addOptions([
]);
$getopt->addOperand(
- Operand::create('gpx', \GetOpt\Operand::MULTIPLE + \GetOpt\Operand::REQUIRED)
+ Operand::create('gpx', Operand::MULTIPLE + Operand::REQUIRED)
->setDescription('One or more GPX files to import')
->setValidation('is_readable', '%s: %s is not readable')
);
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;
}
?>
diff --git a/utils/export.php b/utils/export.php
index f5f1005..8154a2e 100644
--- a/utils/export.php
+++ b/utils/export.php
@@ -123,7 +123,6 @@ if ($trackId && $userId) {
$totalSeconds = 0;
$coordinate = [];
foreach ($positionsArr as $position) {
- /** @var uPosition $prevPosition */
$distance = isset($prevPosition) ? $position->distanceTo($prevPosition) : 0;
$seconds = isset($prevPosition) ? $position->secondsTo($prevPosition) : 0;
$prevPosition = $position;