Remote: SFTP upload via system OpenSSH client
This commit is contained in:
+72
-18
@@ -1,24 +1,78 @@
|
||||
#include "versiond/versiond.hpp"
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <sstream>
|
||||
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<char, 512> 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
|
||||
|
||||
Reference in New Issue
Block a user