Public Access
- uname in the console and /uname in IRC: the firmware, its version and the chip - scp: one file between the card and a trusted server, over SSH, with the device's key or a password asked (masked) in the Shell - shared libssh helpers in src/platform/libssh_util - README, user guide, command reference and a how-to updated Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0162FokPdvY2KsS4NBfwyWPk
82 lines
2.8 KiB
C++
82 lines
2.8 KiB
C++
#include "scp_args.h"
|
|
|
|
#include <vector>
|
|
|
|
namespace roro::term {
|
|
|
|
namespace {
|
|
std::vector<std::string> words(const std::string& text) {
|
|
std::vector<std::string> out;
|
|
for (size_t at = 0; at < text.size();) {
|
|
size_t end = text.find(' ', at);
|
|
if (end == std::string::npos) end = text.size();
|
|
if (end > at) out.push_back(text.substr(at, end - at));
|
|
at = end + 1;
|
|
}
|
|
return out;
|
|
}
|
|
|
|
// user@host:path, as opposed to a path on the card.
|
|
bool isRemote(const std::string& w) {
|
|
if (w.empty() || w[0] == '/') return false;
|
|
size_t at = w.find('@'), colon = w.find(':');
|
|
return at != std::string::npos && colon != std::string::npos && at < colon;
|
|
}
|
|
|
|
std::string baseName(const std::string& path) {
|
|
size_t slash = path.rfind('/');
|
|
return slash == std::string::npos ? path : path.substr(slash + 1);
|
|
}
|
|
} // namespace
|
|
|
|
std::string parseScp(const std::string& args, ScpArgs& out) {
|
|
static const char* kUsage = "scp [-f] [-P port] <file on the card> user@host:path | scp [-f] [-P port] user@host:path <file on the card>";
|
|
std::vector<std::string> w = words(args);
|
|
long port = 0;
|
|
bool replace = false;
|
|
while (!w.empty() && w[0][0] == '-') {
|
|
if (w[0] == "-f") {
|
|
replace = true;
|
|
w.erase(w.begin());
|
|
} else if (w[0] == "-P" && w.size() >= 2) {
|
|
port = 0;
|
|
for (char c : w[1]) {
|
|
if (c < '0' || c > '9' || port > 65535) return "a port from 1 to 65535";
|
|
port = port * 10 + (c - '0');
|
|
}
|
|
if (port < 1 || port > 65535) return "a port from 1 to 65535";
|
|
w.erase(w.begin(), w.begin() + 2);
|
|
} else {
|
|
return kUsage;
|
|
}
|
|
}
|
|
if (w.size() != 2) return kUsage;
|
|
|
|
bool firstRemote = isRemote(w[0]), secondRemote = isRemote(w[1]);
|
|
if (firstRemote == secondRemote) return firstRemote ? "one side has to be on the card, not both on servers" : kUsage;
|
|
|
|
ScpArgs a;
|
|
a.upload = secondRemote;
|
|
a.replace = replace;
|
|
const std::string& remote = a.upload ? w[1] : w[0];
|
|
std::string local = a.upload ? w[0] : w[1];
|
|
size_t colon = remote.find(':');
|
|
std::string why = parseSshTarget(remote.substr(0, colon), a.target);
|
|
if (!why.empty()) return why;
|
|
if (port) a.target.port = static_cast<uint16_t>(port);
|
|
a.remote = remote.substr(colon + 1);
|
|
if (a.remote.empty()) return "say where on " + a.target.host + ": user@host:path";
|
|
if (local.empty() || local[0] != '/') return "a path on the card starts with a slash";
|
|
if (local.back() == '/') {
|
|
if (a.upload) return "that's a folder: scp copies one file";
|
|
std::string name = baseName(a.remote);
|
|
if (name.empty()) return "name the file to fetch";
|
|
local += name;
|
|
}
|
|
a.local = local;
|
|
out = a;
|
|
return "";
|
|
}
|
|
|
|
} // namespace roro::term
|