diff --git a/CMakeLists.txt b/CMakeLists.txt index 3754df3..1d90451 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,14 +13,37 @@ include(GNUInstallDirs) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") -set(PICA_BIN_DIR ${CMAKE_CURRENT_BINARY_DIR}) + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug) +endif() +message("Build type: ${CMAKE_BUILD_TYPE}") + +set(COMPILE_OPTIONS -fno-sized-deallocation) +if (CMAKE_BUILD_TYPE STREQUAL Release) + list(APPEND COMPILE_OPTIONS -O3) +elseif (CMAKE_BUILD_TYPE STREQUAL Debug) + list(APPEND COMPILE_OPTIONS -g) + list(APPEND COMPILE_OPTIONS -Wall) + list(APPEND COMPILE_OPTIONS -Wextra) +endif() + +set(COMPILE_OPTIONS_STRING "") +foreach(element IN LISTS COMPILE_OPTIONS) + if(NOT COMPILE_OPTIONS_STRING STREQUAL "") + set(COMPILE_OPTIONS_STRING "${COMPILE_OPTIONS_STRING} ") + endif() + set(COMPILE_OPTIONS_STRING "${COMPILE_OPTIONS_STRING}${element}") +endforeach() +message("Compile options: " ${COMPILE_OPTIONS_STRING}) find_package(nlohmann_json REQUIRED) find_package(FCGI REQUIRED) -add_executable(pica main.cpp) -target_include_directories(pica PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -target_include_directories(pica PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +add_executable(${PROJECT_NAME} main.cpp) +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +target_compile_options(${PROJECT_NAME} PRIVATE ${COMPILE_OPTIONS}) add_subdirectory(server) add_subdirectory(request) @@ -31,10 +54,10 @@ add_subdirectory(utils) configure_file(config.h.in config.h @ONLY) -target_link_libraries(pica PRIVATE +target_link_libraries(${PROJECT_NAME} PRIVATE FCGI::FCGI FCGI::FCGI++ nlohmann_json::nlohmann_json ) -install(TARGETS pica RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/config.h.in b/config.h.in index 703a9f7..07ee1ea 100644 --- a/config.h.in +++ b/config.h.in @@ -7,3 +7,4 @@ #define BIN_DIR "@CMAKE_INSTALL_BINDIR@" #define PROJECT_NAME "@PROJECT_NAME@" +#define PROJECT_VERSION "@PROJECT_VERSION@" diff --git a/database/CMakeLists.txt b/database/CMakeLists.txt index 8f5b864..42941d6 100644 --- a/database/CMakeLists.txt +++ b/database/CMakeLists.txt @@ -6,7 +6,7 @@ set(SOURCES dbinterface.cpp ) -target_sources(pica PRIVATE ${SOURCES}) +target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) add_subdirectory(mysql) add_subdirectory(migrations) diff --git a/database/migrations/CMakeLists.txt b/database/migrations/CMakeLists.txt index e71b0a4..9091bba 100644 --- a/database/migrations/CMakeLists.txt +++ b/database/migrations/CMakeLists.txt @@ -1,5 +1,5 @@ set(MIGRATIONS migrations) -configure_file(m0.sql ${PICA_BIN_DIR}/${CMAKE_INSTALL_DATADIR}/${MIGRATIONS}/m0.sql COPYONLY) +configure_file(m0.sql ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_DATADIR}/${MIGRATIONS}/m0.sql COPYONLY) install( FILES diff --git a/database/migrations/m0.sql b/database/migrations/m0.sql index 951fa04..53b7028 100644 --- a/database/migrations/m0.sql +++ b/database/migrations/m0.sql @@ -1,6 +1,36 @@ +--creating system table CREATE TABLE IF NOT EXISTS system ( `key` VARCHAR(32) PRIMARY KEY, `value` TEXT ); +--creating roles table +CREATE TABLE IF NOT EXISTS roles ( + `id` INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY, + `name` VARCHAR(256) UNIQUE NOT NULL, + `color` INTEGER UNSIGNED DEFAULT 0 +); + +--creating accounts table +CREATE TABLE IF NOT EXISTS accounts ( + `id` INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY, + `login` VARCHAR(256) UNIQUE NOT NULL, + `nick` VARCHAR(256), + `type` INTEGER UNSIGNED NOT NULL, + `password` VARCHAR(64), + `salt` VARCHAR(32), + `role` INTEGER UNSIGNED NOT NULL, + `created` TIMESTAMP DEFAULT UTC_TIMESTAMP(), + + FOREIGN KEY (role) REFERENCES roles(id) +); + +--creating defailt roles +INSERT IGNORE INTO roles (`name`) +VALUES ('root'); + +--inserting initial version INSERT INTO system (`key`, `value`) VALUES ('version', '0'); + +--recording initial time +INSERT INTO system (`key`, `value`) VALUES ('created', UTC_TIMESTAMP()); diff --git a/database/mysql/CMakeLists.txt b/database/mysql/CMakeLists.txt index 75dd5e1..64d1920 100644 --- a/database/mysql/CMakeLists.txt +++ b/database/mysql/CMakeLists.txt @@ -10,6 +10,6 @@ set(SOURCES find_package(MariaDB REQUIRED) -target_sources(pica PRIVATE ${SOURCES}) +target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) -target_link_libraries(pica PRIVATE MariaDB::client) +target_link_libraries(${PROJECT_NAME} PRIVATE MariaDB::client) diff --git a/database/mysql/mysql.cpp b/database/mysql/mysql.cpp index 6653e25..0532f7c 100644 --- a/database/mysql/mysql.cpp +++ b/database/mysql/mysql.cpp @@ -116,6 +116,14 @@ void MySQL::executeFile(const std::filesystem::path& relativePath) { std::ifstream inputFile(path); std::string query; while (std::getline(inputFile, query, ';')) { + std::optional comment = getComment(query); + while (comment) { + std::cout << '\t' << comment.value() << std::endl; + comment = getComment(query); + } + if (query.empty()) + continue; + int result = mysql_query(con, query.c_str()); if (result != 0) { int errcode = mysql_errno(con); @@ -162,15 +170,49 @@ void MySQL::migrate(uint8_t targetVersion) { uint8_t currentVersion = getVersion(); while (currentVersion < targetVersion) { + if (currentVersion == 255) + throw std::runtime_error("Maximum possible database version reached"); + + uint8_t nextVersion = currentVersion + 1; std::string fileName = "migrations/m" + std::to_string(currentVersion) + ".sql"; std::cout << "Performing migration " << std::to_string(currentVersion) << " -> " - << std::to_string(++currentVersion) + << std::to_string(nextVersion) << std::endl; executeFile(fileName); - setVersion(currentVersion); + setVersion(nextVersion); + currentVersion = nextVersion; } std::cout << "Database is now on actual version " << std::to_string(targetVersion) << std::endl; } + +std::optional MySQL::getComment(std::string& string) { + ltrim(string); + if (string.length() < 2) + return std::nullopt; + + if (string[0] == '-') { + if (string[1] == '-') { + string.erase(0, 2); + std::string::size_type eol = string.find('\n'); + return extract(string, 0, eol); + } + } else if (string[0] == '/') { + if (string[1] == '*') { + string.erase(0, 2); + std::string::size_type end = 0; + do { + end = string.find(end, '*'); + } while (end != std::string::npos && end < string.size() - 1 && string[end + 1] == '/'); + if (end < string.size() - 1) + end = std::string::npos; + + return extract(string, 0, end); + } + } + + return std::nullopt; +} + diff --git a/database/mysql/mysql.h b/database/mysql/mysql.h index 37181a3..007c985 100644 --- a/database/mysql/mysql.h +++ b/database/mysql/mysql.h @@ -5,6 +5,7 @@ #include #include +#include #include @@ -28,6 +29,7 @@ public: private: void executeFile(const std::filesystem::path& relativePath); + static std::optional getComment(std::string& string); protected: MYSQL connection; diff --git a/response/CMakeLists.txt b/response/CMakeLists.txt index 6b35c08..e4c5093 100644 --- a/response/CMakeLists.txt +++ b/response/CMakeLists.txt @@ -6,4 +6,4 @@ set(SOURCES response.cpp ) -target_sources(pica PRIVATE ${SOURCES}) +target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 694b3a5..0e38cbe 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -8,4 +8,4 @@ set(SOURCES router.cpp ) -target_sources(pica PRIVATE ${SOURCES}) +target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) diff --git a/server/server.cpp b/server/server.cpp index 6d1b9a3..f08b408 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -20,18 +20,8 @@ Server::Server(): db->setCredentials("pica", "pica"); db->setDatabase("pica"); - bool connected = false; - try { - db->connect("/run/mysqld/mysqld.sock"); - connected = true; - std::cout << "Successfully connected to the database" << std::endl; - - } catch (const std::runtime_error& e) { - std::cerr << "Couldn't connect to the database: " << e.what() << std::endl; - } - - if (connected) - db->migrate(currentDbVesion); + db->connect("/run/mysqld/mysqld.sock"); + db->migrate(currentDbVesion); router.addRoute("info", Server::info); router.addRoute("env", Server::printEnvironment); @@ -74,7 +64,7 @@ void Server::handleRequest(std::unique_ptr request) { try { std::string path = request->getPath(serverName.value()); router.route(path.data(), std::move(request), this); - } catch (const std::exception e) { + } catch (const std::exception& e) { Response error(Response::Status::internalError); error.setBody(std::string(e.what())); error.replyTo(*request.get()); @@ -82,7 +72,7 @@ void Server::handleRequest(std::unique_ptr request) { } bool Server::printEnvironment(Request* request, Server* server) { - (void)server; + UNUSED(server); nlohmann::json body = nlohmann::json::object(); request->printEnvironment(body); @@ -94,11 +84,11 @@ bool Server::printEnvironment(Request* request, Server* server) { } bool Server::info(Request* request, Server* server) { - (void)server; + UNUSED(server); Response res; nlohmann::json body = nlohmann::json::object(); - body["type"] = "Pica"; - body["version"] = "0.0.1"; + body["type"] = PROJECT_NAME; + body["version"] = PROJECT_VERSION; res.setBody(body); res.replyTo(*request); diff --git a/server/server.h b/server/server.h index dae5b66..f13df21 100644 --- a/server/server.h +++ b/server/server.h @@ -21,6 +21,8 @@ #include "response/response.h" #include "router.h" #include "database/dbinterface.h" +#include "utils/helpers.h" +#include "config.h" class Server { public: diff --git a/stream/CMakeLists.txt b/stream/CMakeLists.txt index 6eef2a8..3fef9b0 100644 --- a/stream/CMakeLists.txt +++ b/stream/CMakeLists.txt @@ -8,4 +8,4 @@ set(SOURCES ostream.cpp ) -target_sources(pica PRIVATE ${SOURCES}) +target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) diff --git a/utils/helpers.cpp b/utils/helpers.cpp index d94a401..c1b93e4 100644 --- a/utils/helpers.cpp +++ b/utils/helpers.cpp @@ -4,12 +4,18 @@ #include "helpers.h" #include "iostream" +#include +#include #include "config.h" static bool installed = false; static std::filesystem::path sPath; +bool isSpace(char ch){ + return std::isspace(static_cast(ch)); +} + void setAbsoluteSharedPath () { installed = true; sPath = FULL_DATA_DIR "/" PROJECT_NAME; // should be something like /usr/share/pica or /local/usr/share/pica @@ -36,7 +42,7 @@ void initPaths(const char* programPath) { if (endsWith(parent.string(), BIN_DIR)) { //this is the case when the program is installed somewhere but not system root std::filesystem::path bin(BIN_DIR); //so it will read from something like ../share/pica/ relative to the binary for (const auto& hop : bin) { - (void)hop; //I do this just to make as many ups as many members are in bin + UNUSED(hop); //I do this just to make as many ups as many members are in bin parent = parent.parent_path(); } sPath = parent / DATA_DIR / PROJECT_NAME; @@ -59,3 +65,40 @@ bool endsWith(const std::string& string, const std::string& query) { return false; } } + +void ltrim(std::string& string) { + string.erase( + string.begin(), + std::find_if( + string.begin(), + string.end(), + std::not_fn(isSpace) + ) + ); +} + +void rtrim(std::string& string) { + string.erase( + std::find_if( + string.rbegin(), + string.rend(), + std::not_fn(isSpace) + ).base(), + string.end() + ); +} + +void trim(std::string& string) { + ltrim(string); + rtrim(string); +} + +std::string extract(std::string& string, std::string::size_type begin, std::string::size_type end) { + std::string result = string.substr(begin, end); + if (end == std::string::npos) + string.erase(begin, end); + else + string.erase(begin, result.length() + 1); + + return result; +} diff --git a/utils/helpers.h b/utils/helpers.h index d9fae3c..34fcd03 100644 --- a/utils/helpers.h +++ b/utils/helpers.h @@ -6,7 +6,12 @@ #include #include +#define UNUSED(variable) (void)variable + void initPaths(const char* programPath); const std::filesystem::path& sharedPath(); bool endsWith(const std::string& string, const std::string& query); - +void ltrim(std::string& string); +void rtrim(std::string& string); +void trim(std::string& string); +std::string extract(std::string& string, std::string::size_type begin, std::string::size_type end);