diff --git a/.docker/init.sh b/.docker/init.sh index 714d920..377ccd1 100644 --- a/.docker/init.sh +++ b/.docker/init.sh @@ -6,6 +6,10 @@ DB_USER_PASS=$2 mkdir -p /run/nginx chown nginx:nginx /run/nginx +# Fix permission issues on mounted volume in macOS +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 @@ -28,7 +32,11 @@ if [ "$ULOGGER_DB_DRIVER" = "pgsql" ]; then su postgres -c "pg_ctl -w stop" sed -i "s/^\$dbdsn = .*$/\$dbdsn = \"pgsql:host=localhost;port=5432;dbname=ulogger\";/" /var/www/html/config.php elif [ "$ULOGGER_DB_DRIVER" = "sqlite" ]; then - sqlite3 /data/ulogger.db < /var/www/html/scripts/ulogger.sqlite + mkdir -p /data + chown nobody:nobody /data + sqlite3 -init /var/www/html/scripts/ulogger.sqlite /data/ulogger.db + sqlite3 -line /data/ulogger.db "INSERT INTO users (login, password) VALUES ('admin', '\$2y\$10\$7OvZrKgonVZM9lkzrTbiou.CVhO3HjPk5y0W9L68fVwPs/osBRIMq')" + sed -i "s/^\$dbdsn = .*$/\$dbdsn = \"sqlite:\/data\/sqlite\/ulogger.db\";/" /var/www/html/config.php else mkdir -p /run/mysqld chown mysql:mysql /run/mysqld diff --git a/.docker/run.sh b/.docker/run.sh index 5dda0fe..3e1856c 100644 --- a/.docker/run.sh +++ b/.docker/run.sh @@ -18,7 +18,7 @@ grep '^\$' /var/www/html/config.php # start services if [ "$ULOGGER_DB_DRIVER" = "pgsql" ]; then su postgres -c 'pg_ctl -D /data start' -else +elif [ "$ULOGGER_DB_DRIVER" = "mysql" ]; then mysqld_safe --datadir=/data & fi nginx diff --git a/.tests/fixtures/fixture_admin.xml b/.tests/fixtures/fixture_admin.xml index a8319bb..2abb77c 100644 --- a/.tests/fixtures/fixture_admin.xml +++ b/.tests/fixtures/fixture_admin.xml @@ -1,17 +1,6 @@ - - - - - - - - - - - 1 - admin - $2y$10$7OvZrKgonVZM9lkzrTbiou.CVhO3HjPk5y0W9L68fVwPs/osBRIMq - - - - + + + + + + diff --git a/.tests/fixtures/fixture_empty.xml b/.tests/fixtures/fixture_empty.xml index 764cc13..4a92520 100644 --- a/.tests/fixtures/fixture_empty.xml +++ b/.tests/fixtures/fixture_empty.xml @@ -1,11 +1,6 @@ - - - - - - - - - - - + + + + + + diff --git a/.tests/lib/BaseDatabaseTestCase.php b/.tests/lib/BaseDatabaseTestCase.php index 5d17f5e..b321b79 100644 --- a/.tests/lib/BaseDatabaseTestCase.php +++ b/.tests/lib/BaseDatabaseTestCase.php @@ -37,7 +37,6 @@ abstract class BaseDatabaseTestCase extends PHPUnit_Extensions_Database_TestCase public function setUp() { parent::setUp(); - } public static function setUpBeforeClass() { @@ -81,15 +80,19 @@ abstract class BaseDatabaseTestCase extends PHPUnit_Extensions_Database_TestCase * @return PHPUnit_Extensions_Database_DataSet_IDataSet */ protected function getDataSet() { - $this->resetSequences(); - return $this->createMySQLXMLDataSet(__DIR__ . '/../fixtures/fixture_empty.xml'); + $this->resetAutoincrement(); + return $this->createFlatXMLDataSet(__DIR__ . '/../fixtures/fixture_empty.xml'); } - protected function resetSequences($users = 1, $tracks = 1, $positions = 1) { + protected function resetAutoincrement($users = 1, $tracks = 1, $positions = 1) { if (self::$driver == "pgsql") { self::$pdo->query("ALTER SEQUENCE users_id_seq RESTART WITH $users"); self::$pdo->query("ALTER SEQUENCE tracks_id_seq RESTART WITH $tracks"); self::$pdo->query("ALTER SEQUENCE positions_id_seq RESTART WITH $positions"); + } else if (self::$driver == "sqlite") { + self::$pdo->query("DELETE FROM sqlite_sequence WHERE NAME = 'users'"); + self::$pdo->query("DELETE FROM sqlite_sequence WHERE NAME = 'tracks'"); + self::$pdo->query("DELETE FROM sqlite_sequence WHERE NAME = 'positions'"); } } @@ -224,7 +227,7 @@ abstract class BaseDatabaseTestCase extends PHPUnit_Extensions_Database_TestCase return "TO_TIMESTAMP($column)"; break; case "sqlite": - return "DATE($column, 'unixepoch')"; + return "DATETIME($column, 'unixepoch')"; break; } } diff --git a/.tests/lib/UloggerAPITestCase.php b/.tests/lib/UloggerAPITestCase.php index b44c986..0bd4fb4 100644 --- a/.tests/lib/UloggerAPITestCase.php +++ b/.tests/lib/UloggerAPITestCase.php @@ -26,8 +26,8 @@ class UloggerAPITestCase extends BaseDatabaseTestCase { } protected function getDataSet() { - $this->resetSequences(2); - return $this->createMySQLXMLDataSet(__DIR__ . '/../fixtures/fixture_admin.xml'); + $this->resetAutoincrement(2); + return $this->createFlatXMLDataSet(__DIR__ . '/../fixtures/fixture_admin.xml'); } /** diff --git a/.tests/tests/ClientAPITest.php b/.tests/tests/ClientAPITest.php index 445a4a1..1b99e8d 100644 --- a/.tests/tests/ClientAPITest.php +++ b/.tests/tests/ClientAPITest.php @@ -43,7 +43,6 @@ class ClientAPITest extends UloggerAPITestCase { public function testAddUser() { $this->assertTrue($this->authenticate(), "Authentication failed"); - $this->assertEquals(1, $this->getConnection()->getRowCount('users'), "Wrong row count"); $options = [ diff --git a/helpers/db.php b/helpers/db.php index 8ec3c5b..195589d 100644 --- a/helpers/db.php +++ b/helpers/db.php @@ -37,6 +37,13 @@ */ protected static $tables; + /** + * Database driver name + * + * @var String Driver + */ + protected static $driver; + /** * PDO constuctor * @@ -52,6 +59,7 @@ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // return assoc array by default ]; @parent::__construct($dsn, $user, $pass, $options); + self::$driver = $this->getAttribute(PDO::ATTR_DRIVER_NAME); $this->setCharset("utf8"); $this->initTables(); } catch (PDOException $e) { @@ -94,8 +102,7 @@ } public function unix_timestamp($column) { - $driver = $this->getAttribute(PDO::ATTR_DRIVER_NAME); - switch ($driver) { + switch (self::$driver) { default: case "mysql": return "UNIX_TIMESTAMP($column)"; @@ -110,8 +117,7 @@ } public function from_unixtime($column) { - $driver = $this->getAttribute(PDO::ATTR_DRIVER_NAME); - switch ($driver) { + switch (self::$driver) { default: case "mysql": return "FROM_UNIXTIME($column)"; @@ -120,13 +126,15 @@ return "TO_TIMESTAMP($column)"; break; case "sqlite": - return "DATE($column, 'unixepoch')"; + return "DATETIME($column, 'unixepoch')"; break; } } private function setCharset($charset) { - $this->query("SET NAMES '$charset'"); + if (self::$driver == "pgsql" || self::$driver == "mysql") { + $this->query("SET NAMES '$charset'"); + } } } ?> diff --git a/helpers/utils.php b/helpers/utils.php index 98b92c7..2fbe3ff 100644 --- a/helpers/utils.php +++ b/helpers/utils.php @@ -142,11 +142,11 @@ } public static function postString($name, $default = NULL) { - if (is_string(($val = self::requestValue($name, $default, INPUT_POST)))) { - return trim($val); - } else { - return $val; - } + return self::requestString($name, $default, INPUT_POST); + } + + public static function getString($name, $default = NULL) { + return self::requestString($name, $default, INPUT_GET); } public static function getBool($name, $default = NULL) { @@ -154,22 +154,30 @@ } public static function postInt($name, $default = NULL) { - if (is_float(($val = self::postFloat($name, $default)))) { - return (int) round($val); - } else { - return self::requestValue($name, $default, INPUT_POST, FILTER_VALIDATE_INT); - } + return self::requestInt($name, $default, INPUT_POST); } public static function getInt($name, $default = NULL) { - if (is_float(($val = self::getFloat($name, $default)))) { - return (int) round($val); + return self::requestInt($name, $default, INPUT_GET); + } + + private static function requestString($name, $default, $type) { + if (is_string(($val = self::requestValue($name, $default, $type)))) { + return trim($val); } else { - return self::requestValue($name, $default, INPUT_GET, FILTER_VALIDATE_INT); + return $val; } } - public static function requestValue($name, $default = NULL, $type = INPUT_POST, $filters = FILTER_DEFAULT, $flags = NULL) { + private static function requestInt($name, $default, $type) { + if (is_float(($val = self::requestValue($name, $default, $type, FILTER_VALIDATE_FLOAT)))) { + return (int) round($val); + } else { + return self::requestValue($name, $default, $type, FILTER_VALIDATE_INT); + } + } + + private static function requestValue($name, $default, $type, $filters = FILTER_DEFAULT, $flags = NULL) { $input = filter_input($type, $name, $filters, $flags); if ($input !== false && !is_null($input)) { return $input; diff --git a/scripts/setup.php b/scripts/setup.php index aa4990b..e3aedff 100644 --- a/scripts/setup.php +++ b/scripts/setup.php @@ -18,7 +18,7 @@ */ // This script is disabled by default. Change below to true before running. -$enabled = true; +$enabled = false; /* -------------------------------------------- */ @@ -31,6 +31,7 @@ if (version_compare(PHP_VERSION, '5.4.0', '<')) { define("ROOT_DIR", dirname(__DIR__)); require_once(ROOT_DIR . "/helpers/user.php"); require_once(ROOT_DIR . "/helpers/config.php"); +require_once(ROOT_DIR . "/helpers/utils.php"); require_once(ROOT_DIR . "/lang.php"); $command = uUtils::postString('command'); @@ -39,6 +40,7 @@ $prefix = preg_replace('/[^a-z0-9_]/i', '', uConfig::$dbprefix); $tPositions = $prefix . "positions"; $tTracks = $prefix . "tracks"; $tUsers = $prefix . "users"; +$dbDriver = null; $messages = []; switch ($command) { @@ -46,7 +48,9 @@ switch ($command) { $error = false; try { - $db = new PDO(uConfig::$dbdsn, uConfig::$dbuser, uConfig::$dbpass); + $options = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION ]; + $pdo = new PDO(uConfig::$dbdsn, uConfig::$dbuser, uConfig::$dbpass, $options); + $dbDriver = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME); } catch (PDOException $e ) { $messages[] = "{$langSetup["dbconnectfailed"]}"; $messages[] = sprintf($langSetup["serversaid"], "" . $e->getMessage() . ""); @@ -54,16 +58,16 @@ switch ($command) { break; } try { - $queries = getQueries($db); + $queries = getQueries($pdo); foreach ($queries as $query) { - $db->query($query); + $pdo->query($query); } } catch (PDOException $e) { $messages[] = "{$langSetup["dbqueryfailed"]}"; $messages[] = sprintf($langSetup["serversaid"], "" . $e->getMessage() . ""); $error = true; } - $db = null; + $pdo = null; if (!$error) { $messages[] = "{$langSetup["dbtablessuccess"]}"; $messages[] = $langSetup["setupuser"]; @@ -114,22 +118,28 @@ switch ($command) { $messages[] = "
"; break; } - if (empty(uConfig::$dbdsn) || empty(uConfig::$dbuser)) { - $messages[] = sprintf($langSetup["nodbsettings"], "\$dbdsn, \$dbuser, \$dbpass"); + 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; } - $messages[] = sprintf($langSetup["scriptdesc"], "'$tPositions', '$tTracks', '$tUsers'", "" . uConfig::$dbname . ""); + $messages[] = sprintf($langSetup["scriptdesc"], "'$tPositions', '$tTracks', '$tUsers'", "" . getDbname(uConfig::$dbdsn) . ""); $messages[] = $langSetup["scriptdesc2"]; $messages[] = "
"; break; } -function getQueries($db) { - $driver = $db->getAttribute(PDO::ATTR_DRIVER_NAME); +function getQueries($pdo) { + global $tPositions, $tUsers, $tTracks, $dbDriver; + $queries = []; - switch($driver) { + switch($dbDriver) { case "mysql": // users $queries[] = "DROP TABLE IF EXISTS `$tUsers`"; @@ -262,6 +272,29 @@ function getQueries($db) { default: throw InvalidArgumentException("Driver not supported"); } + 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"; } ?> diff --git a/scripts/ulogger.sqlite b/scripts/ulogger.sqlite new file mode 100644 index 0000000..2171d96 --- /dev/null +++ b/scripts/ulogger.sqlite @@ -0,0 +1,68 @@ +-- +-- Database: `ulogger` +-- + + +-- -------------------------------------------------------- + +-- +-- Table structure for table `users` +-- + +DROP TABLE IF EXISTS `users`; +CREATE TABLE `users` ( + `id` integer PRIMARY KEY AUTOINCREMENT, + `login` varchar(15) NOT NULL UNIQUE, + `password` varchar(255) NOT NULL DEFAULT '' +); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `tracks` +-- + +DROP TABLE IF EXISTS `tracks`; +CREATE TABLE `tracks` ( + `id` integer PRIMARY KEY AUTOINCREMENT, + `user_id` integer NOT NULL, + `name` varchar(255) DEFAULT NULL, + `comment` varchar(1024) DEFAULT NULL, + FOREIGN KEY(`user_id`) REFERENCES `users`(`id`) +); +CREATE INDEX `idx_user_id` ON `tracks`(`user_id`); + +-- -------------------------------------------------------- + +-- +-- Table structure for table `positions` +-- + +DROP TABLE IF EXISTS `positions`; +CREATE TABLE `positions` ( + `id` integer PRIMARY KEY AUTOINCREMENT, + `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `user_id` integer NOT NULL, + `track_id` integer NOT NULL, + `latitude` double NOT NULL, + `longitude` double NOT NULL, + `altitude` double DEFAULT NULL, + `speed` double DEFAULT NULL, + `bearing` double DEFAULT NULL, + `accuracy` integer DEFAULT NULL, + `provider` varchar(100) DEFAULT NULL, + `comment` varchar(255) DEFAULT NULL, + `image_id` integer DEFAULT NULL, + FOREIGN KEY(`user_id`) REFERENCES `users`(`id`), + FOREIGN KEY(`track_id`) REFERENCES `tracks`(`id`) +); +CREATE INDEX `idx_ptrack_id` ON `positions`(`track_id`); +CREATE INDEX `idx_puser_id` ON `positions`(`user_id`); + +-- +-- This will add default user admin with password admin +-- The password should be changed immediatelly after installation +-- Uncomment if needed +-- +-- INSERT INTO `users` (`id`, `login`, `password`) VALUES +-- (1, 'admin', '$2y$10$7OvZrKgonVZM9lkzrTbiou.CVhO3HjPk5y0W9L68fVwPs/osBRIMq'); diff --git a/utils/export.php b/utils/export.php index db668ed..2f30666 100755 --- a/utils/export.php +++ b/utils/export.php @@ -57,9 +57,9 @@ function toHMS($s) { return (($d > 0) ? "$d d " : "") . sprintf("%02d:%02d:%02d", $h, $m, $s); } -$type = uUtils::postString('type', 'kml'); -$userId = uUtils::postInt('userid'); -$trackId = uUtils::postInt('trackid'); +$type = uUtils::getString('type', 'kml'); +$userId = uUtils::getInt('userid'); +$trackId = uUtils::getInt('trackid'); if (!uConfig::$public_tracks && (!$auth->isAuthenticated() || (!$auth->isAdmin() && $auth->user->id !== $userId))) {