// Copyright (c) FIRST and other WPILib contributors. // Open Source Software; you can modify and/or share it under the terms of // the WPILib BSD license file in the root directory of this project. #include "Sftp.h" #include #include #include #include using namespace sftp; Attributes::Attributes(sftp_attributes&& attr) : name{attr->name}, flags{attr->flags}, type{attr->type}, size{attr->size} { sftp_attributes_free(attr); } static std::string GetError(sftp_session sftp) { switch (sftp_get_error(sftp)) { case SSH_FX_EOF: return "end of file"; case SSH_FX_NO_SUCH_FILE: return "no such file"; case SSH_FX_PERMISSION_DENIED: return "permission denied"; case SSH_FX_FAILURE: return "SFTP failure"; case SSH_FX_BAD_MESSAGE: return "SFTP bad message"; case SSH_FX_NO_CONNECTION: return "SFTP no connection"; case SSH_FX_CONNECTION_LOST: return "SFTP connection lost"; case SSH_FX_OP_UNSUPPORTED: return "SFTP operation unsupported"; case SSH_FX_INVALID_HANDLE: return "SFTP invalid handle"; case SSH_FX_NO_SUCH_PATH: return "no such path"; case SSH_FX_FILE_ALREADY_EXISTS: return "file already exists"; case SSH_FX_WRITE_PROTECT: return "write protected filesystem"; case SSH_FX_NO_MEDIA: return "no media inserted"; default: return ssh_get_error(sftp->session); } } Exception::Exception(sftp_session sftp) : runtime_error{GetError(sftp)}, err{sftp_get_error(sftp)} {} File::~File() { if (m_handle) { sftp_close(m_handle); } } Attributes File::Stat() const { sftp_attributes attr = sftp_fstat(m_handle); if (!attr) { throw Exception{m_handle->sftp}; } return Attributes{std::move(attr)}; } size_t File::Read(void* buf, uint32_t count) { auto rv = sftp_read(m_handle, buf, count); if (rv < 0) { throw Exception{m_handle->sftp}; } return rv; } File::AsyncId File::AsyncReadBegin(uint32_t len) const { int rv = sftp_async_read_begin(m_handle, len); if (rv < 0) { throw Exception{m_handle->sftp}; } return rv; } size_t File::AsyncRead(void* data, uint32_t len, AsyncId id) { auto rv = sftp_async_read(m_handle, data, len, id); if (rv == SSH_ERROR) { throw Exception{ssh_get_error(m_handle->sftp->session)}; } if (rv == SSH_AGAIN) { return 0; } return rv; } size_t File::Write(std::span data) { auto rv = sftp_write(m_handle, data.data(), data.size()); if (rv < 0) { throw Exception{m_handle->sftp}; } return rv; } void File::Seek(uint64_t offset) { if (sftp_seek64(m_handle, offset) < 0) { throw Exception{m_handle->sftp}; } } uint64_t File::Tell() const { return sftp_tell64(m_handle); } void File::Rewind() { sftp_rewind(m_handle); } void File::Sync() { if (sftp_fsync(m_handle) < 0) { throw Exception{m_handle->sftp}; } } Session::Session(std::string_view host, int port, std::string_view user, std::string_view pass) : m_host{host}, m_port{port}, m_username{user}, m_password{pass} { // Create a new SSH session. m_session = ssh_new(); if (!m_session) { throw Exception{"The SSH session could not be allocated."}; } // Set the host, user, and port. ssh_options_set(m_session, SSH_OPTIONS_HOST, m_host.c_str()); ssh_options_set(m_session, SSH_OPTIONS_USER, m_username.c_str()); ssh_options_set(m_session, SSH_OPTIONS_PORT, &m_port); // Set timeout to 3 seconds. int64_t timeout = 3L; ssh_options_set(m_session, SSH_OPTIONS_TIMEOUT, &timeout); // Set other miscellaneous options. ssh_options_set(m_session, SSH_OPTIONS_STRICTHOSTKEYCHECK, "no"); } Session::~Session() { if (m_sftp) { sftp_free(m_sftp); } if (m_session) { ssh_free(m_session); } } void Session::Connect() { // Connect to the server. int rc = ssh_connect(m_session); if (rc != SSH_OK) { throw Exception{ssh_get_error(m_session)}; } // Authenticate with password. rc = ssh_userauth_password(m_session, nullptr, m_password.c_str()); if (rc != SSH_AUTH_SUCCESS) { throw Exception{ssh_get_error(m_session)}; } // Allocate the SFTP session. m_sftp = sftp_new(m_session); if (!m_sftp) { throw Exception{ssh_get_error(m_session)}; } // Initialize. rc = sftp_init(m_sftp); if (rc != SSH_OK) { sftp_free(m_sftp); m_sftp = nullptr; throw Exception{ssh_get_error(m_session)}; } } void Session::Disconnect() { if (m_sftp) { sftp_free(m_sftp); m_sftp = nullptr; } ssh_disconnect(m_session); } std::vector Session::ReadDir(const std::string& path) { sftp_dir dir = sftp_opendir(m_sftp, path.c_str()); if (!dir) { throw Exception{m_sftp}; } std::vector rv; while (sftp_attributes attr = sftp_readdir(m_sftp, dir)) { rv.emplace_back(std::move(attr)); } sftp_closedir(dir); return rv; } void Session::Unlink(const std::string& filename) { if (sftp_unlink(m_sftp, filename.c_str()) < 0) { throw Exception{m_sftp}; } } File Session::Open(const std::string& filename, int accesstype, mode_t mode) { sftp_file f = sftp_open(m_sftp, filename.c_str(), accesstype, mode); if (!f) { throw Exception{m_sftp}; } return File{std::move(f)}; }