// Print your 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); std::ifstream configfile(helpers::get_config_file_path("statusip.cfg")); 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")); } conn.SetHeaders({{"Host", get_host(url)}}); 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() + 10min); } } catch (const std::exception &e) { std::cout << R"()" << e.what() << "" << std::endl; return 33; } }