diff --git a/src/remote.cpp b/src/remote.cpp index d878b36..5502267 100644 --- a/src/remote.cpp +++ b/src/remote.cpp @@ -1,24 +1,78 @@ #include "versiond/versiond.hpp" +#include +#include #include +#include namespace fs = std::filesystem; namespace versiond { -bool remote_upload_wolfssh(const std::string& host, const std::string& user, - const std::string& local, const std::string& remote, - std::string& err) { -#ifdef HAVE_WOLFSSH - (void)host; - (void)user; - (void)local; - (void)remote; +namespace { +struct Target { + std::string user, host, port = "22", path; +}; +Target parse_target(const std::string& url, const std::string& user, const std::string& fallback_remote) { + Target t; + std::string s = url.empty() ? fallback_remote : url; + const std::string pre = "sftp://"; + if (s.rfind(pre, 0) == 0) s = s.substr(pre.size()); + auto at = s.find('@'); + std::string hostpart = s; + if (at != std::string::npos) { + t.user = s.substr(0, at); + hostpart = s.substr(at + 1); + } else { + t.user = user; + } + auto slash = hostpart.find('/'); + std::string hp = slash == std::string::npos ? hostpart : hostpart.substr(0, slash); + std::string p = slash == std::string::npos ? "" : hostpart.substr(slash); + auto colon = hp.rfind(':'); + if (colon != std::string::npos && hp.find(']') == std::string::npos) { + t.host = hp.substr(0, colon); + t.port = hp.substr(colon + 1); + } else { + t.host = hp; + } + t.path = p.empty() ? fallback_remote : p; + return t; +} +std::string shellq(const std::string& s) { + std::string o = "'"; + for (char c : s) { + if (c == '\'') o += "'\\''"; + else o += c; + } + return o + "'"; +} +} // namespace +bool remote_upload_wolfssh(const std::string& host, const std::string& user, const std::string& local, + const std::string& remote, std::string& err) { + if (!fs::exists(local)) { + err = "local file missing: " + local; + return false; + } + Target t = parse_target(host, user, remote); + if (t.host.empty()) { + err = "no remote host"; + return false; + } + std::string dest = t.user.empty() ? t.host : t.user + "@" + t.host; + std::string cmd = "sftp -P " + shellq(t.port) + " -o BatchMode=yes -o StrictHostKeyChecking=accept-new " + + shellq(dest) + " <<'VERSIOND_SFTP_EOF'\nput " + shellq(local) + " " + shellq(t.path) + + "\nVERSIOND_SFTP_EOF 2>&1"; + std::array buf{}; + std::string out; + FILE* p = popen(cmd.c_str(), "r"); + if (!p) { + err = "popen(sftp) failed"; + return false; + } + while (fgets(buf.data(), (int)buf.size(), p)) out += buf.data(); + int rc = pclose(p); + if (rc != 0) { + err = "sftp exit " + std::to_string(rc) + ": " + out; + return false; + } err = ""; - return fs::exists(local); -#else - (void)host; - (void)user; - (void)local; - (void)remote; - err = "wolfssh not vendored yet (deps/wolfssh empty)"; - return false; -#endif -} + return true; } +} // namespace versiond