diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4524b1c..32ef521 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,49 +1,95 @@ -image: luki42/dynuiprefresher_build:latest - stages: - cmake - build - - build_package - - test - -cache: - paths: - - build/ - - src/ - - inc/ - - tests/ - - postinst + - post +# Unix Build cmake: stage: cmake + image: luki42/dynuiprefresher_build:latest script: - cmake -S . -B build + artifacts: + paths: + - build/ + - inc/ + - postinst build: stage: build + image: luki42/dynuiprefresher_build:latest script: - cd build - - make + - make dynuiprefresher artifacts: paths: - - "build/bin/*" + - build/ + - inc/ + - postinst + dependencies: + - cmake build_package: - stage: build_package + stage: post + image: luki42/dynuiprefresher_build:latest script: - cd build - make package artifacts: paths: - - "build/packages/*" + - build/ + - inc/ + - postinst + dependencies: + - build test: - stage: test + stage: post + image: luki42/dynuiprefresher_build:latest script: - cd build - - make test - - make build-xml + - make build-test artifacts: reports: - junit: build/report.xml \ No newline at end of file + junit: build/*.xml + dependencies: + - build + +# Windows Build +cmake_win64: + stage: cmake + image: luki42/dynuiprefresher_build:windows + script: + - cmake -S . -B build -D WinBuild=ON + artifacts: + paths: + - build/ + - inc/ + +build_win64: + stage: build + image: luki42/dynuiprefresher_build:windows + script: + - cd build + - make dynuiprefresher + artifacts: + paths: + - build/ + - inc/ + dependencies: + - cmake_win64 + + +build_package_win64: + stage: post + image: luki42/dynuiprefresher_build:windows + script: + - cd build + - make package + artifacts: + paths: + - build/ + - inc/ + dependencies: + - build_win64 \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index dcf4afc..a8b6bb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,8 @@ # for test build gtest needs to be installed. cmake_minimum_required(VERSION 3.13) -project(iprefresher DESCRIPTION "Dynu ip refresher") -SET(PROJECT_VERSION 1.3.3) +project(dynuiprefresher DESCRIPTION "Dynu ip refresher" LANGUAGES) +SET(PROJECT_VERSION 1.3.4) # CONFIGURATION SET(CMAKE_BUILD_TYPE Release) # manually SET build type (Release / Debug) @@ -20,11 +20,12 @@ option(BUILD_DOC "Build documentation" ON) # additional dependency for Doxygen option(PACKAGING "Allow Packaging to , or " ON) # additional dependencies for RPMbuild,dpkg or NSIS option(TESTS "Build Tests" ON) # additional dependencies for GTEST - to build tests option(GUI "Build GUI elements" ON) # additional dependencies to QT libraries needed -set(WinBuild false) +option(WinBuild "cross compile for Windows Platform" OFF) # helper variables SET(CMAKE_CXX_STANDARD 17) string(TIMESTAMP TIMESTAMP_NOW "%d.%m.%Y") +SET(Application_Name "dynuiprefresher") SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) @@ -32,6 +33,7 @@ SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) # setup winbuild compilers if (${WinBuild}) + message(STATUS "setup Mingw Toolchain for cross compile.") set(LIBSUFFIX .dll) set(SUFFIX .exe) @@ -41,9 +43,9 @@ if (${WinBuild}) #set(TOOLCHAIN_PREFIX i686-w64-mingw32) # cross compilers to use for C and C++ - set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) - set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) - set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) + set(CMAKE_C_COMPILER /usr/bin/${TOOLCHAIN_PREFIX}-gcc) + set(CMAKE_CXX_COMPILER /usr/bin/${TOOLCHAIN_PREFIX}-g++) + set(CMAKE_RC_COMPILER /usr/bin/${TOOLCHAIN_PREFIX}-windres) # target environment on the build host system set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) @@ -65,18 +67,31 @@ if (${WinBuild}) # or set(CMAKE_CXX_STANDARD_LIBRARIES -lcurl -lpthread -static-libgcc -static-libstdc++ -lcrypto -lssl -lws2_32 -static -DCURL_STATICLIB) # or add_definitions(-DCURL_STATICLIB) - # windows config path is same as executable - set(CONFIG_PATH "./iprefresher.cfg") + # windows config path is in %appdata% folder of user + set(CONFIG_PATH "std::string(std::getenv(\"USERPROFILE\")) + \"\\\\AppData\\\\Roaming\\\\DynuIPrefresher\\\\\"") + # temp file is also stored in appdata folder + set(TempFilePath "std::string(std::getenv(\"USERPROFILE\")) + \"\\\\AppData\\\\Roaming\\\\DynuIPrefresher\\\\\"") else () + message(STATUS "using nativ gcc toolchain.") set(LIBSUFFIX .so) set(SUFFIX "") - # set /etc/ config path - set(CONFIG_PATH "/etc/iprefresher.cfg") + # set linux config path + set(CONFIG_PATH "\"/etc/\"") + # set path of temp file + set(TempFilePath "\"/var/tmp/\"") endif () +# test compiler settings and enable languages here +message("") +message(STATUS "Testing the C++ compiler!") +enable_language(CXX) +message("") +message(STATUS "Testing the C compiler!") +enable_language(C) # config libs +message("") message(STATUS "Config of Libraries") # libcurl if (${WinBuild}) @@ -89,7 +104,7 @@ if (${WinBuild}) message(STATUS "Using CURL lib(s): ${CURL_LIBRARIES}") message(STATUS "") - include_directories(${CURL_INCLUDE_DIRS} inc) + include_directories(${CURL_INCLUDE_DIRS}) # configure libconfig FIND_PATH(LIBCONFIG++_INCLUDE_DIRS libconfig.h++ /usr/${TOOLCHAIN_PREFIX}/sys-root/mingw/include/) # search for libconfig include headers @@ -98,11 +113,11 @@ if (${WinBuild}) message(STATUS "Using LIBCONFIG++ include dir(s): ${LIBCONFIG++_INCLUDE_DIRS}") message(STATUS "Using LIBCONFIG++ lib(s): ${LIBCONFIG++_LIBRARIES}") - include_directories(${LIBCONFIG++_INCLUDE_DIRS} inc) - if (${GUI}) set(CMAKE_PREFIX_PATH "/usr/${TOOLCHAIN_PREFIX}/sys-root/mingw/lib/cmake") endif () + + include_directories(${LIBCONFIG++_INCLUDE_DIRS}) else () find_package(CURL REQUIRED) if (CURL_INCLUDE_DIRS AND CURL_LIBRARIES) @@ -112,7 +127,7 @@ else () else () message(FATAL_ERROR "Could not find CURL") endif () - include_directories(${CURL_INCLUDE_DIR} inc) + include_directories(${CURL_INCLUDE_DIR}) message("") # libconfig @@ -134,6 +149,7 @@ else () include_directories(${LIBCONFIG_INCLUDE_DIRS}) endif () + if (${GUI}) set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -143,14 +159,16 @@ if (${GUI}) find_package(Qt5Sql REQUIRED) endif () +include_directories(inc) + message("") #read sample config -FILE(READ ${CMAKE_SOURCE_DIR}/config/iprefresher.cfg SAMPLECONFIG) -#add version header -FILE(WRITE ${CMAKE_SOURCE_DIR}/inc/Version.h +FILE(READ ${CMAKE_SOURCE_DIR}/config/dynuiprefresher.cfg SAMPLECONFIG) +#add StaticData header +FILE(WRITE ${CMAKE_SOURCE_DIR}/inc/StaticData.h "/** - * Version header to store Version, Config dir and a Sample config + * StaticData header to store Version, Config dir and a Sample config * Do not edit this file manually, it is generated by the cmake script! * * @author Lukas Heiligenbrunner @@ -161,9 +179,11 @@ FILE(WRITE ${CMAKE_SOURCE_DIR}/inc/Version.h #include -namespace Version { +namespace StaticData { const std::string VERSION = \"${PROJECT_VERSION}\"; - const std::string ConfigDir = \"${CONFIG_PATH}\"; + const std::string TempFilePath = ${TempFilePath}; + const std::string ConfigDir = ${CONFIG_PATH}; + const std::string ConfName = \"${Application_Name}.cfg\"; const std::string SAMPLECONFIG = R\"(${SAMPLECONFIG})\"; }" ) @@ -174,7 +194,7 @@ add_library(api ${LIB_METHOD} src/api/DynuAPI.cpp src/api/IPAPI.cpp) -add_library(dynuiprefresher ${LIB_METHOD} +add_library(libdynuiprefresher ${LIB_METHOD} src/IPRefresher.cpp src/Config.cpp src/IpHelper.cpp @@ -182,10 +202,10 @@ add_library(dynuiprefresher ${LIB_METHOD} src/Logger.cpp ) -add_executable(iprefresher src/main.cpp) +add_executable(${Application_Name} src/main.cpp) # LINK generated LIBS # -target_link_libraries(iprefresher dynuiprefresher api ${CURL_LIBRARIES} ${LIBCONFIG++_LIBRARIES}) +target_link_libraries(${Application_Name} libdynuiprefresher api ${CURL_LIBRARIES} ${LIBCONFIG++_LIBRARIES}) if (${GUI}) set(QT5_LIBRARIES Qt5::Widgets Qt5::PrintSupport Qt5::Sql) @@ -220,9 +240,9 @@ IF (NOT ${WinBuild}) # INSTALL to Linux SYSTEM # # install binaries - install(TARGETS iprefresher DESTINATION usr/bin) + install(TARGETS ${Application_Name} DESTINATION usr/bin) # install systemd service and enable it - install(FILES service/iprefresher.service DESTINATION lib/systemd/system) + install(FILES service/${Application_Name}.service DESTINATION lib/systemd/system) if (${GUI}) # install binaries @@ -232,8 +252,8 @@ ELSE () # INSTALL to Windows SYSTEM # # install binary to current folder - set_target_properties(iprefresher PROPERTIES SUFFIX ".exe") - install(TARGETS iprefresher DESTINATION .) + set_target_properties(${Application_Name} PROPERTIES SUFFIX ".exe") + install(TARGETS ${Application_Name} DESTINATION .) # install .dll dependencies # todo check if files exist... @@ -301,8 +321,10 @@ if (${PACKAGING}) if [ ! -f ${CONFIG_PATH} ]; then cat > ${CONFIG_PATH} <<- EOM ${SAMPLECONFIG}EOM -fi\n" - ) +fi + +systemctl enable ${Application_Name}.service +systemctl start ${Application_Name}.service") SET(CPACK_DEB_COMPONENT_INSTALL 1) @@ -371,8 +393,8 @@ fi\n" add_custom_target(build-packages "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --target package - DEPENDS ${PROJECT_NAME} - COMMENT "Packing ${PROJECT_NAME}") + DEPENDS ${Application_Name} + COMMENT "Packing ${Application_Name}") message("") ENDIF () @@ -403,39 +425,40 @@ endif (BUILD_DOC) # Test Cases if (TESTS) - include(GoogleTest) + # include(GoogleTest) + message(STATUS "Configuring GTEST") + find_package(GTest) + if (GTEST_FOUND) - mark_as_advanced( - BUILD_GMOCK BUILD_GTEST BUILD_SHARED_LIBS - gmock_build_tests gtest_build_samples gtest_build_tests - gtest_disable_pthreads gtest_force_shared_crt gtest_hide_internal_symbols - ) + mark_as_advanced( + BUILD_GMOCK BUILD_GTEST BUILD_SHARED_LIBS + gmock_build_tests gtest_build_samples gtest_build_tests + gtest_disable_pthreads gtest_force_shared_crt gtest_hide_internal_symbols + ) - enable_testing() + enable_testing() - macro(package_add_test TESTNAME) - # create an exectuable in which the tests will be stored - add_executable(${TESTNAME} ${ARGN}) - # link the Google test infrastructure, mocking library, and a default main fuction to - target_link_libraries(${TESTNAME} gtest gtest_main -lpthread -lm dynuiprefresher api ${CURL_LIBRARIES} ${LIBCONFIG++_LIBRARIES}) - # see https://cmake.org/cmake/help/v3.10/module/GoogleTest.html for more options to pass to it - gtest_discover_tests(${TESTNAME} - WORKING_DIRECTORY ${PROJECT_DIR} - EXTRA_ARGS --gtest_output=xml:report.xml -VV - PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "${PROJECT_DIR}" - ) - set_target_properties(${TESTNAME} PROPERTIES FOLDER tests) - endmacro() + macro(package_add_test TESTNAME) + # create an exectuable in which the tests will be stored + add_executable(${TESTNAME} ${ARGN}) + # link the Google test infrastructure, mocking library, and a default main fuction to + target_link_libraries(${TESTNAME} gtest gtest_main -lpthread -lm libdynuiprefresher api ${CURL_LIBRARIES} ${LIBCONFIG++_LIBRARIES}) + # see https://cmake.org/cmake/help/v3.10/module/GoogleTest.html for more options to pass to it + gtest_discover_tests(${TESTNAME}) - package_add_test(test1 tests/UnitTest.cpp ${SOURCE}) + add_custom_command(TARGET build-test + POST_BUILD + COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --target ${TESTNAME} + COMMAND "bin/${TESTNAME}" --gtest_output=xml:${TESTNAME}-report.xml) + endmacro() - add_custom_target(build-test - "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" --target test - DEPENDS ${PROJECT_NAME} - COMMENT "Packing ${PROJECT_NAME}") + add_custom_target(build-test + DEPENDS ${PROJECT_NAME} + COMMENT "Testing ${PROJECT_NAME}") - add_custom_target(build-xml - "bin/test1" --gtest_output="xml:report.xml" - DEPENDS ${PROJECT_NAME} - COMMENT "Packing ${PROJECT_NAME}") + package_add_test(test1 tests/UnitTest.cpp ${SOURCE}) + + else () + message(STATUS "GTEST environment not found!") + endif () ENDIF () \ No newline at end of file diff --git a/README.md b/README.md index 06e556a..bf7f869 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,27 @@ I'm providing executables for Debian/Ubuntu (.deb) and RHEL/Debian (.rpm). But you can still compile the code my your own (see build section). ## Configuration -There is a configuration file `/etc/iprefresher.cfg` where you have to specify the DYNU API key (get it from their homepage), the domainid and your domain. +There is a configuration file `/etc/dynuiprefresher.cfg` where you have to specify the DYNU API key (get it from their homepage), the domainid and your domain. Furthermore, you can optionally specify a Telegram API key and a Chat ID if you want to be notfied when your local ip changes. To enable and start the service: -`systemctl enable iprefresher.service` and `systemctl start iprefresher.service` +`systemctl enable dynuiprefresher.service` and `systemctl start dynuiprefresher.service` + +## Usage +First of all configure the right keys in the `/etc/dynuiprefresher.cfg` or `%appdata%\DynuIpRefresher\dynuiprefresher.cfg` config file. + +Afterwards use the following options: + +``` +help page: +[-h] [--help] print this help page +[-v] [--version] print the software version +[-f] [--force] force refresh of ip +[-l] [--loop] infinite loop to refresh ip every five minutes +[-c] [--checkconfig] validate configuration +[-ip] [--currentip] get current global ip +[no argument] normal ip check and refresh +``` ## Build diff --git a/config/iprefresher.cfg b/config/dynuiprefresher.cfg similarity index 100% rename from config/iprefresher.cfg rename to config/dynuiprefresher.cfg diff --git a/service/iprefresher.service b/service/dynuiprefresher.service similarity index 100% rename from service/iprefresher.service rename to service/dynuiprefresher.service diff --git a/src/Config.cpp b/src/Config.cpp index 882f5fe..d68aea7 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -1,11 +1,12 @@ #include "Config.h" #include "Logger.h" -#include "Version.h" +#include "StaticData.h" #include #include #include #include +#include std::string Config::dynuapikey; std::string Config::domainid; //id of the dynu domain @@ -19,15 +20,41 @@ bool Config::telegramSupport; bool Config::readConfig() { libconfig::Config cfg; try { - cfg.readFile(Version::ConfigDir.c_str()); + cfg.readFile(std::string(StaticData::ConfigDir + StaticData::ConfName).c_str()); } catch (const libconfig::FileIOException &fioex) { std::cout << "I/O error while reading config file." << std::endl << "creating new config file!" << std::endl; + // check if config folder exists + struct stat info{}; + + if (stat(StaticData::ConfigDir.c_str(), &info) != 0) { + Logger::warning("The config folder doesn't exist. Trying to create it."); + +// mkdir command is different defined for windows +#ifdef __unix + int check = mkdir(StaticData::ConfigDir.c_str(), 777); +#else + int check = mkdir(StaticData::ConfigDir.c_str()); +#endif + + // check if directory is created or not + if (!check) + Logger::message("config directory successfully created. "); + else + Logger::error("unable to create config directory."); + + } else if (info.st_mode & S_IFDIR) { + Logger::debug("config directory exists already"); + } else { + Logger::error("A file exists with the same name as the config dir should be"); + } + + std::ofstream myfile; - myfile.open(Version::ConfigDir); + myfile.open(StaticData::ConfigDir + StaticData::ConfName); if (myfile.is_open()) { - myfile << Version::SAMPLECONFIG; + myfile << StaticData::SAMPLECONFIG; myfile.close(); } else { Logger::error("error creating file"); @@ -73,7 +100,7 @@ bool Config::validateConfig() { libconfig::Config cfg; try { Logger::message("reading config file"); - cfg.readFile(Version::ConfigDir.c_str()); + cfg.readFile(StaticData::ConfigDir.c_str()); } catch (const libconfig::FileIOException &fioex) { Logger::warning("config file doesn't exist or permission denied!"); diff --git a/src/FileLogger.cpp b/src/FileLogger.cpp index 78d670c..0e9caf0 100644 --- a/src/FileLogger.cpp +++ b/src/FileLogger.cpp @@ -1,12 +1,13 @@ #include "FileLogger.h" #include "IpHelper.h" +#include "StaticData.h" #include #include void FileLogger::safeip(std::string ip) { std::ofstream out; - out.open("ip.txt", std::ios::out); + out.open(StaticData::TempFilePath + "temp-dynuiprefresher.txt", std::ios::out); out << ip; @@ -15,7 +16,7 @@ void FileLogger::safeip(std::string ip) { std::string FileLogger::readip() { std::ifstream in; - in.open("ip.txt", std::ios::in); + in.open(StaticData::TempFilePath + "temp-dynuiprefresher.txt", std::ios::in); std::string ip; diff --git a/src/IPRefresher.cpp b/src/IPRefresher.cpp index 17e7ba5..ee4d7fa 100644 --- a/src/IPRefresher.cpp +++ b/src/IPRefresher.cpp @@ -4,7 +4,7 @@ #include "api/DynuAPI.h" #include "api/TelegramAPI.h" #include "Config.h" -#include "Version.h" +#include "StaticData.h" #include "IpHelper.h" #include @@ -59,15 +59,18 @@ bool IPRefresher::checkIPAdress(bool force) { void IPRefresher::startUpService(int interval) { Logger::message("startup of service"); - Logger::message("Version: " + Version::VERSION); + Logger::message("Version: " + StaticData::VERSION); if (Config::readConfig()) { while (true) { Logger::message("starting check"); - checkIPAdress(false); + if (Config::readConfig()) { + checkIPAdress(false); + } else { + std::cout << "incorrect credentials!" << std::endl; + } std::this_thread::sleep_for(std::chrono::milliseconds(interval * 1000)); } } else { std::cout << "incorrect credentials!" << std::endl; } - } diff --git a/src/IpHelper.cpp b/src/IpHelper.cpp index 67c5384..5be0bf0 100644 --- a/src/IpHelper.cpp +++ b/src/IpHelper.cpp @@ -1,7 +1,5 @@ #include "IpHelper.h" -#include - bool IpHelper::isIpValid(std::string ip) { - return (ip.find('.') != ULONG_MAX); + return (ip.find('.') != SIZE_MAX); } diff --git a/src/api/TelegramAPI.cpp b/src/api/TelegramAPI.cpp index c89a247..4cc3acf 100644 --- a/src/api/TelegramAPI.cpp +++ b/src/api/TelegramAPI.cpp @@ -1,8 +1,6 @@ #include "api/TelegramAPI.h" #include "Logger.h" -#include - int TelegramAPI::sendMessage(const std::string &text) { Hashmap args; args.add("chat_id", chatid); @@ -12,8 +10,8 @@ int TelegramAPI::sendMessage(const std::string &text) { std::string reply = request("https://api.telegram.org/bot" + apikey + "/sendmessage", false, args, headers); - if (reply.find("\"error_code\"") != ULONG_MAX) { - Logger::error("failed to refresh the ip (Dynu API)"); + if (reply.find("\"error_code\"") != SIZE_MAX) { + Logger::error("failed to send the Telegram Message"); return -1; } return 1; diff --git a/src/main.cpp b/src/main.cpp index e16b7d1..8e55acd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,4 @@ -#include "Version.h" +#include "StaticData.h" #include "IPRefresher.h" #include "Logger.h" #include "Config.h" @@ -19,7 +19,7 @@ int main(int argc, char *argv[]) { << "[-ip] [--currentip] get current global ip" << std::endl << "[no argument] normal ip check and refresh" << std::endl; } else if (firstarg == "-v" || firstarg == "--version") { - std::cout << "Version " << Version::VERSION << std::endl; + std::cout << "Version " << StaticData::VERSION << std::endl; } else if (firstarg == "-f" || firstarg == "--force") { if (Config::readConfig()) { IPRefresher::checkIPAdress(true); diff --git a/tests/UnitTest.cpp b/tests/UnitTest.cpp index e1d5bcc..2c97a9e 100644 --- a/tests/UnitTest.cpp +++ b/tests/UnitTest.cpp @@ -4,7 +4,6 @@ #include #include -#include #include "gtest/gtest.h" /** @@ -21,7 +20,7 @@ TEST(ReadIp, testzeroIpIfNotExists) { TEST(IPAPI, testIpAPIcheckIPSyntax) { IPAPI ipapi; std::string ip = ipapi.getGlobalIp(); - if (ip.find('.') == ULONG_MAX) { + if (ip.find('.') == SIZE_MAX) { // error when ip doesn't contain a . ASSERT_TRUE(false); } else {