From dea22ddbe04e72c600524838d3d0f4b8169c85ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Gro=C3=9F?= Date: Mon, 25 Mar 2024 21:42:43 +0100 Subject: [PATCH] Refactor remote support This moves the logic into a separate file and also implements proper caching. --- src/Util/util.cpp | 77 -------------------------------- src/Util/util.hpp | 3 -- src/path.cpp | 11 +---- src/remote.cpp | 111 ++++++++++++++++++++++++++++++++++++++++++++++ src/remote.hpp | 23 ++++++++++ 5 files changed, 136 insertions(+), 89 deletions(-) create mode 100644 src/remote.cpp create mode 100644 src/remote.hpp diff --git a/src/Util/util.cpp b/src/Util/util.cpp index 51be7a8..386e8b4 100644 --- a/src/Util/util.cpp +++ b/src/Util/util.cpp @@ -6,9 +6,7 @@ #include #ifdef Q_OS_UNIX -#include #include -#include #endif namespace Util { @@ -34,79 +32,4 @@ std::string pwd() { } return result; } - -bool get_local_domain(std::string &result) { - static std::string cached_result; - if (!cached_result.empty()) { - result = cached_result; - return true; - } - -#ifndef Q_OS_UNIX - return false; -#else - char host[HOST_NAME_MAX]; - if (gethostname(host, HOST_NAME_MAX)) { - std::cerr << "gethostname failed" << std::endl; - return false; - } - - // got the host, try to get the FQDN - struct addrinfo hints, *res; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_CANONNAME; - - int err = getaddrinfo(host, "http", &hints, &res); - if (err) { - std::cerr << "getaddrinfo failed: " << gai_strerror(err) << std::endl; - return false; - } - - const auto fqdn = res->ai_canonname; - bool success = true; - - if (!strncmp(fqdn, host, HOST_NAME_MAX)) { - std::cerr << "failed to retreive a proper FQDN for " << host << std::endl; - success = false; - } else { - result = fqdn; - - // remove possible leading dot - if (result.starts_with('.')) { - result = result.substr(1); - } - - cached_result = result; - } - - freeaddrinfo(res); - return success; -#endif -} - -std::string get_username() { -#ifdef Q_OS_UNIX - return getlogin(); -#else - return {}; -#endif -} - -int get_port() { - const auto env = std::getenv("SSH_CONNECTION"); - if (env) { - std::string ssh_connection {env}; - const auto n = ssh_connection.rfind(' '); - if (n != std::string::npos) { - try { - return std::stoi(ssh_connection.substr(n + 1)); - } catch (std::invalid_argument const &) { - std::cerr << "Failed to parse port number " << ssh_connection << std::endl; - } - } - } - return -1; -} } diff --git a/src/Util/util.hpp b/src/Util/util.hpp index 1c97b57..4b0c02a 100644 --- a/src/Util/util.hpp +++ b/src/Util/util.hpp @@ -14,7 +14,4 @@ namespace Util { const char *home_dir(); std::string pwd(); -bool get_local_domain(std::string &result); -std::string get_username(); -int get_port(); } diff --git a/src/path.cpp b/src/path.cpp index 1651840..43a847b 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -4,6 +4,7 @@ #include "Util/util.hpp" #include "mimedb.hpp" +#include "remote.hpp" #include "settings.hpp" Path::Path(const std::string &p) @@ -30,15 +31,7 @@ QUrl Path::get_url() const { auto res = QUrl::fromLocalFile(QString::fromStdString(path.string())); if (Settings::get()->remote) { - std::string host; - if (Util::get_local_domain(host)) { - res.setScheme("sftp"); - res.setUserName(QString::fromStdString(Util::get_username())); - res.setHost(QString::fromStdString(host)); - res.setPort(Util::get_port()); - } else { - std::cerr << "Failed deducing remote prefix" << std::endl; - } + std::ignore = Remote::get()->rewire_url(res); } return res; diff --git a/src/remote.cpp b/src/remote.cpp new file mode 100644 index 0000000..d72b221 --- /dev/null +++ b/src/remote.cpp @@ -0,0 +1,111 @@ +#include "remote.hpp" + +#include + +#ifdef Q_OS_UNIX +#include +#include +#endif + +bool Remote::rewire_url(QUrl &url) { + if (!init_done) { + init(); + } + if (!ok) { + return false; + } + + url.setScheme("sftp"); + url.setUserName(username); + url.setHost(host); + url.setPort(port); + + return true; +} + +void Remote::init() { + init_done = true; + + username = get_username(); + if (username.isEmpty()) { + std::cerr << "Could not read username" << std::endl; + return; + } + + host = get_local_domain(); + if (host.isEmpty()) { + std::cerr << "Could not read host" << std::endl; + return; + } + + // not a big deal if this fails, usually it still works without an explicit port + port = get_port(); + + ok = true; +} + +QString Remote::get_local_domain() { +#ifndef Q_OS_UNIX + return {}; +#else + char host[HOST_NAME_MAX]; + if (gethostname(host, HOST_NAME_MAX)) { + std::cerr << "gethostname failed" << std::endl; + return {}; + } + + // got the host, try to get the FQDN + struct addrinfo hints, *res; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + + int err = getaddrinfo(host, "http", &hints, &res); + if (err) { + std::cerr << "getaddrinfo failed: " << gai_strerror(err) << std::endl; + return {}; + } + + const auto fqdn = res->ai_canonname; + std::string result; + + if (!strncmp(fqdn, host, HOST_NAME_MAX)) { + std::cerr << "failed to retreive a proper FQDN for " << host << std::endl; + } else { + result = fqdn; + + // remove possible leading dot + if (result.starts_with('.')) { + result = result.substr(1); + } + } + + freeaddrinfo(res); + return QString::fromStdString(result); +#endif +} + +QString Remote::get_username() { +#ifdef Q_OS_UNIX + return getlogin(); +#else + return {}; +#endif +} + +int Remote::get_port() { + const auto env = std::getenv("SSH_CONNECTION"); + if (env) { + std::string ssh_connection {env}; + const auto n = ssh_connection.rfind(' '); + if (n != std::string::npos) { + try { + return std::stoi(ssh_connection.substr(n + 1)); + } catch (std::invalid_argument const &) { + std::cerr << "Failed to parse port number " << ssh_connection << std::endl; + } + } + } + return -1; +} diff --git a/src/remote.hpp b/src/remote.hpp new file mode 100644 index 0000000..ff0a5dc --- /dev/null +++ b/src/remote.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include "Util/util.hpp" + +class Remote { +public: + SINGLETON(Remote) + bool rewire_url(QUrl &url); +private: + void init(); + QString get_local_domain(); + QString get_username(); + int get_port(); + + bool init_done = false; + bool ok = false; + + QString username; + QString host; + int port = -1; +};