From f5bef72217d486a5ee1a53df70b82ab51b286247 Mon Sep 17 00:00:00 2001 From: tastytea Date: Tue, 24 Aug 2021 16:01:39 +0200 Subject: [PATCH] Add statusip. --- CMakeLists.txt | 7 ++- statusip.cpp | 159 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 statusip.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 767737d..5bb1561 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,5 +25,8 @@ add_executable(statusweather "statusweather.cpp" "helpers.cpp") target_link_libraries(statusweather PRIVATE fmt::fmt restclient-cpp nlohmann_json::nlohmann_json Threads::Threads - Boost::program_options -) + Boost::program_options) + +add_executable(statusip "statusip.cpp" "helpers.cpp") +target_link_libraries(statusip + PRIVATE fmt::fmt Boost::program_options restclient-cpp) diff --git a/statusip.cpp b/statusip.cpp new file mode 100644 index 0000000..04ce123 --- /dev/null +++ b/statusip.cpp @@ -0,0 +1,159 @@ +// Print yout IP(s) for i3 status bar. +/* i3blocks config: + * [ip] + * command=statusip --url= + * interval=persist + * markup=pango + */ + +#include "helpers.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +std::string get_url(int argc, char *argv[]) +{ + namespace po = boost::program_options; + + po::options_description options("Options"); + // clang-format off + options.add_options() + ("help,h", "Display this help and exit.") + ("url", po::value()->required()->value_name("URL"), + "URL that returns your IP address as plain text."); + // clang-format on + + po::variables_map vm; + po::store(po::command_line_parser(argc, argv).options(options).run(), vm); + + const auto path{[]() + { + auto path{helpers::get_env("XDG_CONFIG_HOME")}; + if (path.empty()) + { + path = helpers::get_env("HOME"); + if (!path.empty()) + { + path += "/.config"; + } + } + return path += "/statusip.cfg"; + }()}; + std::ifstream configfile(path); + po::store(po::parse_config_file(configfile, options, true), vm); + configfile.close(); + + if (vm.count("help") != 0) + { + std::cout << options; + std::exit(0); // NOLINT(concurrency-mt-unsafe) + } + po::notify(vm); + + return vm["url"].as(); +} + +std::string format_responses(const std::string_view ipv6, + const std::string_view ipv4) +{ + using fmt::format; + + std::string out; + + if (size_t pos{0}; (pos = ipv6.find(':')) != std::string::npos) + { + for (int n = 0; n < 4; ++n) + { + pos = ipv6.find(':', pos); + ++pos; + } + out += format(R"({:s})" + R"({:s})", + ipv6.substr(0, pos), ipv6.substr(pos)); + } + if (ipv4.find('.') != std::string::npos) + { + (out += " ") += ipv4; + } + + return out; +} + +std::string get_host(const std::string_view url) +{ + const size_t pos{url.find("//") + 2}; + return std::string(url.substr(pos, url.find('/', pos) - pos)); +} + +// Ughf. 🙈 +std::string get_ipv4(const std::string_view url) +{ + // NOLINTNEXTLINE(concurrency-mt-unsafe) + auto *hostent{gethostbyname(get_host(url).data())}; + // NOLINTNEXTLINE + auto *addr = reinterpret_cast(hostent->h_addr_list[0]); + return inet_ntoa(addr[0]); // NOLINT +} + +int main(int argc, char *argv[]) +{ + using clock = std::chrono::system_clock; + using namespace std::chrono_literals; + + try + { + const auto url{get_url(argc, argv)}; + std::string ipv6; + std::string ipv4; + + while (true) + { + RestClient::init(); + RestClient::Connection conn(""); + // We can't force IPv4 or IPv6? + // + conn.FollowRedirects(true, 5); + conn.SetTimeout(120); + + auto response{conn.get(url)}; + if (response.code == 200) + { + const auto &body{response.body}; + ipv6 = body.substr(0, body.find_first_of("\r\n")); + } + + RestClient::HeaderFields headers{{"Host", get_host(url)}}; + conn.SetHeaders(headers); + response = conn.get(get_ipv4(url)); + if (response.code == 200) + { + const auto &body{response.body}; + ipv4 = body.substr(0, body.find_first_of("\r\n")); + } + + RestClient::disable(); + + std::cout << format_responses(ipv6, ipv4) << std::endl; + std::this_thread::sleep_until(clock::now() + 30min); + } + } + catch (const std::exception &e) + { + std::cout << R"()" << e.what() << "" + << std::endl; + return 33; + } +}