bettola/common/src/lua_api.cpp

307 lines
9.3 KiB
C++

#include <sol/call.hpp>
#include <sol/types.hpp>
#include <sstream>
#include "i_network_bridge.h"
#include "lua_api.h"
#include "session.h"
#include "machine.h"
#include "vfs.h"
#include "util.h"
namespace api {
vfs_node* get_current_dir(Session& context) {
return context.get_current_dir();
}
std::string rm(Session& context, const std::string& filename) {
vfs_node* current_dir = context.get_current_dir();
auto it = current_dir->children.find(filename);
if(it == current_dir->children.end()) {
return "rm: cannot remove '" + filename + "': No such file or directory.";
}
if(it->second->type == DIR_NODE) {
return "rm: cannot remove '" + filename + "': Is a directory.";
}
delete it->second; /* Free the memory for the node. */
current_dir->children.erase(it); /* Remove from map. */
return "";
}
std::string write_file(Session& context, const std::string& path,
const std::string& content) {
vfs_node* parent_dir = nullptr;
std::string filename;
if(path[0] == '/') {
Machine* session_machine = context.get_session_machine();
vfs_node* root = session_machine->vfs_root;
size_t last_slash = path.find_last_of('/');
if(last_slash == 0) {
/* File in root. */
parent_dir = root;
filename = path.substr(1);
} else {
std::string parent_path = path.substr(0, last_slash);
parent_dir = find_node_by_path(root, parent_path);
filename = path.substr(last_slash+1);
}
} else {
/* Relative path. */
parent_dir = context.get_current_dir();
filename = path;
}
if(!parent_dir) {
return "write: cannot create file '" + path + "': No such file or directory";
}
if(parent_dir->type != DIR_NODE) {
return "write: cannot create file in '" + get_full_path(parent_dir) + "': Not a directory";
}
auto it = parent_dir->children.find(filename);
if(it != parent_dir->children.end()) {
if(it->second->type == DIR_NODE) {
return "write: " + path + ": Is a directory";
}
it->second->content = content;
} else {
/* File does not exist, create it. */
vfs_node* new_file = new_node(filename, FILE_NODE, parent_dir);
new_file->content = content;
parent_dir->children[filename] = new_file;
}
return "";
}
std::string create_executable(Session& context, const std::string& path,
const std::string& content) {
vfs_node* parent_dir = nullptr;
std::string filename;
if(path[0] == '/') {
Machine* session_machine = context.get_session_machine();
vfs_node* root = session_machine->vfs_root;
size_t last_slash = path.find_last_of('/');
if(last_slash == 0) {
parent_dir = root;
filename = path.substr(1);
} else {
std::string parent_path = path.substr(0, last_slash);
parent_dir = find_node_by_path(root, parent_path);
filename = path.substr(last_slash+1);
}
} else {
parent_dir = context.get_current_dir();
filename = path;
}
if(!parent_dir) {
return "create_executable: cannot create file '" + path + "': No such file or directory";
}
if(parent_dir->type != DIR_NODE) {
return "create_executable: cannot create file in '" + get_full_path(parent_dir)
+ "': Not a directory";
}
/* Overwrite if exists. */
auto it = parent_dir->children.find(filename);
if(it != parent_dir->children.end()) {
delete it->second;
parent_dir->children.erase(it);
}
vfs_node* new_exec = new_node(filename, EXEC_NODE, parent_dir);
new_exec->content = util::xor_string(content);
parent_dir->children[filename] = new_exec;
return "";
}
std::string cd(Session& context, const std::string& path) {
vfs_node* current_dir = context.get_current_dir();
if(path == "..") {
if(current_dir->parent) {
context.set_current_dir(current_dir->parent);
}
} else {
auto it = current_dir->children.find(path);
if(it != current_dir->children.end() && it->second->type == DIR_NODE) {
context.set_current_dir(it->second);
} else {
return "cd: no such file or directory: " + path;
}
}
return "";
}
std::string ls(Session& context) {
vfs_node* dir = context.get_current_dir();
if(dir->type != DIR_NODE) {
return "ls: not a directory";
}
std::stringstream ss;
for(auto const& [name, node] : dir->children) {
ss << name;
if(node->type == DIR_NODE) {
ss << "/";
}
ss << " ";
}
return ss.str();
}
std::string ssh(Session& context, const std::string& ip) {
INetworkBridge* bridge = context.get_network_bridge();
Machine* target_machine = bridge->get_machine_by_ip(ip);
if(target_machine) {
context.set_session_machine(target_machine);
return "Connected to " + ip;
} else {
return "ssh: Could not resolve hostname " + ip + ": Name or service not found";
}
}
std::string nmap(Session& context, const std::string& ip) {
INetworkBridge* bridge = context.get_network_bridge();
Machine* target_machine = bridge->get_machine_by_ip(ip);
if(!target_machine) {
return "nmap: Could not resolve host: " + ip;
}
/* Read services from the cached machine object, not the DB. */
auto services = target_machine->services;
/* Release the machine from the cache if it's a remote machine. */
if(target_machine != context.get_session_machine()) {
bridge->release_machine(target_machine->id);
}
if(services.empty()) {
return "No open ports for " + ip;
}
std::stringstream ss;
ss << "Host: " << ip << "\n";
ss << "PORT\tSTATE\tSERVICE\n";
for(auto const& [port, service_name] : services) {
ss << port << "/tcp\t" << "open\t" << service_name << "\n";
}
return ss.str();
}
std::string disconnect(Session& context) {
Machine* current_machine = context.get_session_machine();
if(current_machine != context.get_home_machine()) {
INetworkBridge* bridge = context.get_network_bridge();
bridge->release_machine(current_machine->id);
}
context.set_session_machine(context.get_home_machine());
return "Connection closed.";
}
std::string close_terminal(Session& context) {
return "__CLOSE_CONNECTION__";
}
struct ScpPath {
std::string host;
std::string path;
};
ScpPath parse_scp_path(const std::string& arg) {
size_t colon_pos = arg.find(':');
if(colon_pos != std::string::npos) {
return {arg.substr(0, colon_pos), arg.substr(colon_pos+1)};
} else {
return {"", arg};
}
}
std::string scp(Session& context, const std::string& source_arg,
const std::string& dest_arg) {
ScpPath source_path = parse_scp_path(source_arg);
ScpPath dest_path = parse_scp_path(dest_arg);
Machine* session_machine = context.get_session_machine();
INetworkBridge* bridge = context.get_network_bridge();
Machine* source_machine = source_path.host.empty()
? session_machine : bridge->get_machine_by_ip(source_path.host);
Machine* dest_machine = dest_path.host.empty()
? session_machine : bridge->get_machine_by_ip(dest_path.host);
if(!source_machine) {
return "scp: " + source_path.host + ": Name or service not known";
}
if(!dest_machine) {
if(source_machine != session_machine) bridge->release_machine(source_machine->id);
return "scp: " + dest_path.host + ": Name or service not known";
}
/* Simplified to use only absolute paths for now. */
vfs_node* source_node = find_node_by_path(source_machine->vfs_root, source_path.path);
if(!source_node || source_node->type != FILE_NODE) {
if(source_machine != session_machine) bridge->release_machine(source_machine->id);
if(dest_machine != session_machine) bridge->release_machine(dest_machine->id);
return "scp: " + source_path.path + ": No such file or directory";
}
vfs_node* dest_node = find_node_by_path(dest_machine->vfs_root, dest_path.path);
vfs_node* dest_dir = nullptr;
std::string dest_filename;
if(dest_node && dest_node->type == DIR_NODE) {
dest_dir = dest_node;
dest_filename = source_node->name;
} else {
size_t last_slash = dest_path.path.find_last_of('/');
if(last_slash != std::string::npos) {
std::string parent_path = dest_path.path.substr(0, last_slash);
if(parent_path.empty()) parent_path = "/";
dest_dir = find_node_by_path(dest_machine->vfs_root, parent_path);
dest_filename = dest_path.path.substr(last_slash+1);
} else {
dest_dir = dest_machine->vfs_root;
dest_filename = dest_path.path;
}
}
if(!dest_dir) {
if(source_machine != session_machine) bridge->release_machine(source_machine->id);
if(dest_machine != session_machine) bridge->release_machine(dest_machine->id);
return "scp: " + dest_path.path + ": No such file or directory";
}
if(dest_dir->children.count(dest_filename)) {
vfs_node* existing_node = dest_dir->children[dest_filename];
if(existing_node->type == DIR_NODE) {
if(source_machine != session_machine) bridge->release_machine(source_machine->id);
if(dest_machine != session_machine) bridge->release_machine(dest_machine->id);
return "scp: " + dest_path.path + ": Is a directory";
}
existing_node->content = source_node->content;
} else {
vfs_node* new_file = new_node(dest_filename, FILE_NODE, dest_dir);
new_file->content = source_node->content;
dest_dir->children[dest_filename] = new_file;
}
if(source_machine != session_machine) bridge->release_machine(source_machine->id);
if(dest_machine != session_machine) bridge->release_machine(dest_machine->id);
return "";
}
} /* namespace api */