diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e4a5ca..1953939 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,6 +136,7 @@ namespace StaticData { }" ) +#define libraries with sources here add_library(api ${LIB_METHOD} src/api/API.cpp src/api/TelegramAPI.cpp @@ -147,12 +148,14 @@ add_library(libdynuiprefresher ${LIB_METHOD} src/IpHelper.cpp src/FileLogger.cpp src/Logger.cpp - src/ConfigParser.cpp) + src/Config.cpp) add_executable(${Application_Name} src/main.cpp) +message(${CONAN_LIBS}) + # LINK generated LIBS # -target_link_libraries(${Application_Name} libdynuiprefresher api CONAN_PKG::libcurl) +target_link_libraries(${Application_Name} libdynuiprefresher api CONAN_PKG::libcurl CONAN_PKG::LibConfig) if (${GUI}) set(QT5_LIBRARIES Qt5::Widgets Qt5::PrintSupport Qt5::Sql) @@ -179,7 +182,7 @@ if (${GUI}) endif () # LINK generated LIBS # - target_link_libraries(${Application_Name}-gui -lpthread libdynuiprefresher api CONAN_PKG::libcurl ${QT5_LIBRARIES}) + target_link_libraries(${Application_Name}-gui -lpthread libdynuiprefresher api CONAN_PKG::libcurl CONAN_PKG::LibConfig ${QT5_LIBRARIES}) endif () # setting install targets diff --git a/conanfile.txt b/conanfile.txt index eb064c5..8e33719 100644 --- a/conanfile.txt +++ b/conanfile.txt @@ -1,5 +1,6 @@ [requires] - libcurl/7.72.0@heili/release + libcurl/7.72.0@prebuiltconanbinaries/stable + LibConfig/1.7.2@prebuiltconanbinaries/stable [generators] cmake diff --git a/inc/Config.h b/inc/Config.h new file mode 100644 index 0000000..db806e7 --- /dev/null +++ b/inc/Config.h @@ -0,0 +1,113 @@ +/** + * A static class to manage the configuration file, read/write parameters to it. + * + * @author Lukas Heiligenbrunner + * @date 11.02.2020 + */ + +#pragma once + +#include + +class Config { +public: + /** + * read configuration out of config file + * + * @return success of config read + */ + static bool readConfig(); + + /** + * save back configuration to file + * + * @return success of config write + */ + static bool saveConfig(); + + /** + * validate config file + * + * @return validity of config file + */ + static bool validateConfig(); + + /** + * check if telegram credentials in config are set + * @return is supported? + */ + static bool isTelegramSupported(); + + /** Getters **/ + + /** + * encapsulated getter for DynuApiKey + * @return api key + */ + static const std::string &getDynuapikey(); + + /** + * encapsulated getter for DomainId + * @return DomainId + */ + static const std::string &getDomainid(); + + /** + * encapsulated getter for Domainname + * @return Domainname + */ + static const std::string &getDomainname(); + + /** + * encapsulated getter for TelegramApiKey + * @return TelegramApiKey + */ + static const std::string &getTelegramApiKey(); + + /** + * encapsulated getter for ChatId + * @return ChatId + */ + static const std::string &getChatId(); + + /** + * set all parameters without telegram support + * + * @param domainname Dynu Domain name + * @param dynuapikey Dynu api key + * @param domainid Dynu domain id + */ + static void setValues(const std::string &domainname, const std::string &dynuapikey, const std::string &domainid); + + /** + * set all parameters with telegram support + * + * @param domainname Dynu Domain name + * @param dynuapikey Dynu api key + * @param domainid Dynu domain id + * @param telegramApiKey Telegram api key + * @param chatId Telegram chat id + */ + static void setValues(const std::string &domainname, const std::string &dynuapikey, const std::string &domainid, + const std::string &telegramApiKey, const std::string &chatId); + +private: + /** + * private constructor --> don't allow instance of this class + */ + Config() = default; + + /** + * helper variable for managing telegram Support + */ + static bool telegramSupport; + + /** + * helper variables for storing keys and ids + */ + static std::string dynuapikey; + static std::string domainid; //id of the dynu domain + static std::string domainname; + static std::string telegramApiKey; + static std::string chatId; +}; \ No newline at end of file diff --git a/inc/ConfigParser.h b/inc/ConfigParser.h deleted file mode 100644 index 8f4dc6a..0000000 --- a/inc/ConfigParser.h +++ /dev/null @@ -1,51 +0,0 @@ -// -// Created by lukas on 09.10.20. -// - -#pragma once - - -class ConfigParser { -public: - static bool loadConfig(); - bool saveConfig(); - static bool validateConfig(); - - /** - * check if telegram credentials in config are set - * @return is supported? - */ - static bool isTelegramSupported(); - - /** Getters **/ - - /** - * encapsulated getter for DynuApiKey - * @return api key - */ - static const std::string &getDynuapikey(); - - /** - * encapsulated getter for DomainId - * @return DomainId - */ - static const std::string &getDomainid(); - - /** - * encapsulated getter for Domainname - * @return Domainname - */ - static const std::string &getDomainname(); - - /** - * encapsulated getter for TelegramApiKey - * @return TelegramApiKey - */ - static const std::string &getTelegramApiKey(); - - /** - * encapsulated getter for ChatId - * @return ChatId - */ - static const std::string &getChatId(); -}; diff --git a/inc/IpHelper.h b/inc/IpHelper.h index 8b30ba9..4051051 100644 --- a/inc/IpHelper.h +++ b/inc/IpHelper.h @@ -16,7 +16,7 @@ public: * @param ip ip address to test * @return validity */ - static bool isIpValid(std::string ip); + static bool isIpValid(const std::string& ip); private: }; diff --git a/src/Config.cpp b/src/Config.cpp new file mode 100644 index 0000000..e51d84e --- /dev/null +++ b/src/Config.cpp @@ -0,0 +1,182 @@ +#include "Config.h" +#include "Logger.h" +#include "StaticData.h" + +#include +#include +#include +#include +#include + +std::string Config::dynuapikey; +std::string Config::domainid; //id of the dynu domain +std::string Config::domainname; + +std::string Config::telegramApiKey; +std::string Config::chatId; + +bool Config::telegramSupport; + +bool Config::readConfig() { + libconfig::Config cfg; + try { + 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(StaticData::ConfigDir + StaticData::ConfName); + if (myfile.is_open()) { + myfile << StaticData::SAMPLECONFIG; + myfile.close(); + } else { + Logger::error("error creating file"); + } + + return false; + } + catch (const libconfig::ParseException &pex) { + std::cerr << "Parse error at " << pex.getFile() << ":" << pex.getLine() + << " - " << pex.getError() << std::endl; + return false; + } + + try { + // needed parameters + dynuapikey = (std::string) cfg.lookup("dynuapikey"); + domainid = (std::string) cfg.lookup("domainid"); + domainname = (std::string) cfg.lookup("domainname"); + // optional parameters + telegramApiKey = (std::string) cfg.lookup("telegramApiKey"); + chatId = (std::string) cfg.lookup("chatId"); + telegramSupport = true; + } + catch (const libconfig::SettingNotFoundException &nfex) { + // triggered if setting is missing in config + if (!(std::strcmp("telegramApiKey", nfex.getPath()) == 0 || std::strcmp("chatId", nfex.getPath()) == 0)) { + std::cerr << "No '" << nfex.getPath() << "' setting in configuration file." << std::endl; + } else { + Logger::message("no Telegram support - fields in config not set"); + telegramSupport = false; + } + } + // check if needed values aren't empty + return !(Config::dynuapikey.empty() || Config::domainid.empty() || Config::domainname.empty()); +} + +bool Config::saveConfig() { + // todo save config + return false; +} + +bool Config::validateConfig() { + libconfig::Config cfg; + try { + Logger::message("reading config file: " + std::string(StaticData::ConfigDir + StaticData::ConfName)); + cfg.readFile(std::string(StaticData::ConfigDir + StaticData::ConfName).c_str()); + } + catch (const libconfig::FileIOException &fioex) { + Logger::warning("config file doesn't exist or permission denied!"); + return false; + } + catch (const libconfig::ParseException &pex) { + std::cerr << "Parse error at " << pex.getFile() << ":" << pex.getLine() + << " - " << pex.getError() << std::endl; + return false; + } + Logger::message("Syntax and Permission is OK"); + + try { + // needed parameters + if (((std::string) cfg.lookup("dynuapikey")).empty()) { + Logger::warning("required parameter \"dynuapikey\" seems to be empty."); + return false; + } + if (((std::string) cfg.lookup("domainid")).empty()) { + Logger::warning("required parameter \"domainid\" seems to be empty."); + return false; + } + if (((std::string) cfg.lookup("domainname")).empty()) { + Logger::warning("required parameter \"domainname\" seems to be empty."); + return false; + } + // optional parameters + cfg.lookup("telegramApiKey"); + cfg.lookup("chatId"); + telegramSupport = true; + } + catch (const libconfig::SettingNotFoundException &nfex) { + // triggered if setting is missing in config + if (!(std::strcmp("telegramApiKey", nfex.getPath()) == 0 || std::strcmp("chatId", nfex.getPath()) == 0)) { + std::cerr << "No '" << nfex.getPath() << "' setting in configuration file." << std::endl; + return false; + } else { + Logger::message("no Telegram support - fields in config not set"); + telegramSupport = false; + } + } + return true; +} + +bool Config::isTelegramSupported() { + return telegramSupport; +} + +const std::string &Config::getDynuapikey() { + return dynuapikey; +} + +const std::string &Config::getDomainid() { + return domainid; +} + +const std::string &Config::getDomainname() { + return domainname; +} + +const std::string &Config::getTelegramApiKey() { + return telegramApiKey; +} + +const std::string &Config::getChatId() { + return chatId; +} + +void Config::setValues(const std::string &domainname, const std::string &dynuapikey, const std::string &domainid) { + Config::domainname = domainname; + Config::dynuapikey = dynuapikey; + Config::domainid = domainid; +} + +void Config::setValues(const std::string &domainname, const std::string &dynuapikey, const std::string &domainid, const std::string &telegramApiKey, const std::string &chatId) { + setValues(domainname, dynuapikey, domainid); + Config::telegramApiKey = telegramApiKey; + Config::chatId = chatId; +} diff --git a/src/ConfigParser.cpp b/src/ConfigParser.cpp deleted file mode 100644 index faefd25..0000000 --- a/src/ConfigParser.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// -// Created by lukas on 09.10.20. -// - -#include -#include -#include -#include -#include -#include "inc/ConfigParser.h" - -bool ConfigParser::loadConfig() { - const std::string config = StaticData::ConfigDir + StaticData::ConfName; - - const std::regex matchcomment(R"(^\s*#)"); // match hash to be a comment line - const std::regex matchkey(R"(.+(?=\=.+))"); - const std::regex matchvalue(R"((?:=(.+)(?=\s*#*)))"); - - std::map entries; - - std::ifstream myfile(config); - if (myfile.is_open()) { - std::string line; - - while (getline(myfile, line)) { - if (std::regex_search(line, matchcomment) || line == "") { - // comment line - continue; - } - - // parse a key value pair - std::smatch mk, mv; - std::regex_search(line, mk, matchkey); - std::regex_search(line, mv, matchvalue); - - if (!mk.empty() && !mv.empty()){ - entries.insert(std::pair(mk[0], mv[0])); - std::cout << mk[0] << "--" << mv[0] << std::endl; - } - - - } - myfile.close(); - } else return false; - - return true; -} - -bool ConfigParser::saveConfig() { - // todo - return false; -} - -bool ConfigParser::validateConfig() { - // todo - return false; -} - -bool ConfigParser::isTelegramSupported() { - return false; -} - -const std::string &ConfigParser::getDynuapikey() { - return ""; -} - -const std::string &ConfigParser::getDomainid() { - return ""; -} - -const std::string &ConfigParser::getDomainname() { - return ""; -} - -const std::string &ConfigParser::getTelegramApiKey() { - return ""; -} - -const std::string &ConfigParser::getChatId() { - return ""; -} diff --git a/src/IPRefresher.cpp b/src/IPRefresher.cpp index 2744a19..230cf86 100644 --- a/src/IPRefresher.cpp +++ b/src/IPRefresher.cpp @@ -3,13 +3,13 @@ #include "api/IPAPI.h" #include "api/DynuAPI.h" #include "api/TelegramAPI.h" +#include "Config.h" #include "StaticData.h" #include "IpHelper.h" #include #include #include -#include bool IPRefresher::checkIPAdress(bool force) { FileLogger logger; @@ -35,13 +35,13 @@ bool IPRefresher::checkIPAdress(bool force) { Logger::message("ip changed! -- from :" + oldip + "to: " + ip); DynuAPI dynu; - dynu.init(ConfigParser::getDynuapikey(), ConfigParser::getDomainid(), ConfigParser::getDomainname()); + dynu.init(Config::getDynuapikey(), Config::getDomainid(), Config::getDomainname()); // actual refresh of IP in api - here bool result = dynu.refreshIp(ip); - if (result && ConfigParser::isTelegramSupported()) { + if (result && Config::isTelegramSupported()) { TelegramAPI tele; - tele.init(ConfigParser::getTelegramApiKey(), ConfigParser::getChatId()); + tele.init(Config::getTelegramApiKey(), Config::getChatId()); tele.sendMessage(oldip + " moved to " + ip); } else if (!result) { //error @@ -61,7 +61,7 @@ void IPRefresher::startUpService(int interval) { while (true) { Logger::message("starting check"); - if (ConfigParser::loadConfig()) { + if (Config::readConfig()) { checkIPAdress(false); } else { std::cout << "incorrect credentials!" << std::endl; diff --git a/src/IpHelper.cpp b/src/IpHelper.cpp index 5be0bf0..c9a2f1c 100644 --- a/src/IpHelper.cpp +++ b/src/IpHelper.cpp @@ -1,5 +1,7 @@ +#include #include "IpHelper.h" -bool IpHelper::isIpValid(std::string ip) { - return (ip.find('.') != SIZE_MAX); +bool IpHelper::isIpValid(const std::string& ip) { + const std::regex rgx(R"(^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$)"); + return (std::regex_match(ip, rgx)); } diff --git a/src/api/IPAPI.cpp b/src/api/IPAPI.cpp index ec079b8..11723fd 100644 --- a/src/api/IPAPI.cpp +++ b/src/api/IPAPI.cpp @@ -1,5 +1,11 @@ +#include +#include #include "api/IPAPI.h" std::string IPAPI::getGlobalIp() { - return request("https://api.ipify.org"); + const std::string ip = request("https://api.ipify.org"); + if(!IpHelper::isIpValid(ip)) + Logger::warning("no valid ip returned from ipapi"); + + return ip; } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 52035a3..3070c4e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,8 @@ #include "StaticData.h" #include "IPRefresher.h" #include "Logger.h" +#include "Config.h" #include "api/IPAPI.h" -#include "ConfigParser.h" /** * application entry point @@ -21,7 +21,7 @@ int main(int argc, char *argv[]) { } else if (firstarg == "-v" || firstarg == "--version") { std::cout << "Version " << StaticData::VERSION << std::endl; } else if (firstarg == "-f" || firstarg == "--force") { - if (ConfigParser::loadConfig()) { + if (Config::readConfig()) { IPRefresher::checkIPAdress(true); } else { std::cout << "incorrect credentials!" << std::endl; @@ -30,7 +30,7 @@ int main(int argc, char *argv[]) { } else if (firstarg == "-l" || firstarg == "--loop") { IPRefresher::startUpService(true); } else if (firstarg == "-c" || firstarg == "--checkconfig") { - if (ConfigParser::validateConfig()) { + if (Config::validateConfig()) { Logger::message("Config file is OK"); } else { Logger::error("There are errors in config file!"); @@ -38,16 +38,15 @@ int main(int argc, char *argv[]) { } } else if (firstarg == "-ip" || firstarg == "--currentip") { IPAPI ipapi; - std::cout << "Current global IP: " << ipapi.getGlobalIp() << std::endl; + const std::string ip = ipapi.getGlobalIp(); + + std::cout << "Current global IP: " << ip << std::endl; } else { Logger::message("wrong arguments! -h for help"); } } else { - ConfigParser::loadConfig(); - - return 0; Logger::message("starting check"); - if (ConfigParser::loadConfig()) { + if (Config::readConfig()) { IPRefresher::checkIPAdress(false); } else { std::cout << "incorrect credentials!" << std::endl;