blob: 3a33d13afe386352d059cdbb1c32e9a14ec085d2 [file] [log] [blame]
James Kuszmaulcf324122023-01-14 14:07:17 -08001// Copyright (c) FIRST and other WPILib contributors.
2// Open Source Software; you can modify and/or share it under the terms of
3// the WPILib BSD license file in the root directory of this project.
4
5#include "Sftp.h"
6
7#include <fmt/format.h>
8
9using namespace sftp;
10
11Attributes::Attributes(sftp_attributes&& attr)
12 : name{attr->name}, flags{attr->flags}, type{attr->type}, size{attr->size} {
13 sftp_attributes_free(attr);
14}
15
16static std::string GetError(sftp_session sftp) {
17 switch (sftp_get_error(sftp)) {
18 case SSH_FX_EOF:
19 return "end of file";
20 case SSH_FX_NO_SUCH_FILE:
21 return "no such file";
22 case SSH_FX_PERMISSION_DENIED:
23 return "permission denied";
24 case SSH_FX_FAILURE:
25 return "SFTP failure";
26 case SSH_FX_BAD_MESSAGE:
27 return "SFTP bad message";
28 case SSH_FX_NO_CONNECTION:
29 return "SFTP no connection";
30 case SSH_FX_CONNECTION_LOST:
31 return "SFTP connection lost";
32 case SSH_FX_OP_UNSUPPORTED:
33 return "SFTP operation unsupported";
34 case SSH_FX_INVALID_HANDLE:
35 return "SFTP invalid handle";
36 case SSH_FX_NO_SUCH_PATH:
37 return "no such path";
38 case SSH_FX_FILE_ALREADY_EXISTS:
39 return "file already exists";
40 case SSH_FX_WRITE_PROTECT:
41 return "write protected filesystem";
42 case SSH_FX_NO_MEDIA:
43 return "no media inserted";
44 default:
45 return ssh_get_error(sftp->session);
46 }
47}
48
49Exception::Exception(sftp_session sftp)
50 : runtime_error{GetError(sftp)}, err{sftp_get_error(sftp)} {}
51
52File::~File() {
53 if (m_handle) {
54 sftp_close(m_handle);
55 }
56}
57
58Attributes File::Stat() const {
59 sftp_attributes attr = sftp_fstat(m_handle);
60 if (!attr) {
61 throw Exception{m_handle->sftp};
62 }
63 return Attributes{std::move(attr)};
64}
65
66size_t File::Read(void* buf, uint32_t count) {
67 auto rv = sftp_read(m_handle, buf, count);
68 if (rv < 0) {
69 throw Exception{m_handle->sftp};
70 }
71 return rv;
72}
73
74File::AsyncId File::AsyncReadBegin(uint32_t len) const {
75 int rv = sftp_async_read_begin(m_handle, len);
76 if (rv < 0) {
77 throw Exception{m_handle->sftp};
78 }
79 return rv;
80}
81
82size_t File::AsyncRead(void* data, uint32_t len, AsyncId id) {
83 auto rv = sftp_async_read(m_handle, data, len, id);
84 if (rv == SSH_ERROR) {
85 throw Exception{ssh_get_error(m_handle->sftp->session)};
86 }
87 if (rv == SSH_AGAIN) {
88 return 0;
89 }
90 return rv;
91}
92
93size_t File::Write(std::span<const uint8_t> data) {
94 auto rv = sftp_write(m_handle, data.data(), data.size());
95 if (rv < 0) {
96 throw Exception{m_handle->sftp};
97 }
98 return rv;
99}
100
101void File::Seek(uint64_t offset) {
102 if (sftp_seek64(m_handle, offset) < 0) {
103 throw Exception{m_handle->sftp};
104 }
105}
106
107uint64_t File::Tell() const {
108 return sftp_tell64(m_handle);
109}
110
111void File::Rewind() {
112 sftp_rewind(m_handle);
113}
114
115void File::Sync() {
116 if (sftp_fsync(m_handle) < 0) {
117 throw Exception{m_handle->sftp};
118 }
119}
120
121Session::Session(std::string_view host, int port, std::string_view user,
122 std::string_view pass)
123 : m_host{host}, m_port{port}, m_username{user}, m_password{pass} {
124 // Create a new SSH session.
125 m_session = ssh_new();
126 if (!m_session) {
127 throw Exception{"The SSH session could not be allocated."};
128 }
129
130 // Set the host, user, and port.
131 ssh_options_set(m_session, SSH_OPTIONS_HOST, m_host.c_str());
132 ssh_options_set(m_session, SSH_OPTIONS_USER, m_username.c_str());
133 ssh_options_set(m_session, SSH_OPTIONS_PORT, &m_port);
134
135 // Set timeout to 3 seconds.
136 int64_t timeout = 3L;
137 ssh_options_set(m_session, SSH_OPTIONS_TIMEOUT, &timeout);
138
139 // Set other miscellaneous options.
140 ssh_options_set(m_session, SSH_OPTIONS_STRICTHOSTKEYCHECK, "no");
141}
142
143Session::~Session() {
144 if (m_sftp) {
145 sftp_free(m_sftp);
146 }
147 if (m_session) {
148 ssh_free(m_session);
149 }
150}
151
152void Session::Connect() {
153 // Connect to the server.
154 int rc = ssh_connect(m_session);
155 if (rc != SSH_OK) {
156 throw Exception{ssh_get_error(m_session)};
157 }
158
159 // Authenticate with password.
160 rc = ssh_userauth_password(m_session, nullptr, m_password.c_str());
161 if (rc != SSH_AUTH_SUCCESS) {
162 throw Exception{ssh_get_error(m_session)};
163 }
164
165 // Allocate the SFTP session.
166 m_sftp = sftp_new(m_session);
167 if (!m_sftp) {
168 throw Exception{ssh_get_error(m_session)};
169 }
170
171 // Initialize.
172 rc = sftp_init(m_sftp);
173 if (rc != SSH_OK) {
174 sftp_free(m_sftp);
175 m_sftp = nullptr;
176 throw Exception{ssh_get_error(m_session)};
177 }
178}
179
180void Session::Disconnect() {
181 if (m_sftp) {
182 sftp_free(m_sftp);
183 m_sftp = nullptr;
184 }
185 ssh_disconnect(m_session);
186}
187
188std::vector<Attributes> Session::ReadDir(const std::string& path) {
189 sftp_dir dir = sftp_opendir(m_sftp, path.c_str());
190 if (!dir) {
191 throw Exception{m_sftp};
192 }
193
194 std::vector<Attributes> rv;
195 while (sftp_attributes attr = sftp_readdir(m_sftp, dir)) {
196 rv.emplace_back(std::move(attr));
197 }
198
199 sftp_closedir(dir);
200 return rv;
201}
202
203void Session::Unlink(const std::string& filename) {
204 if (sftp_unlink(m_sftp, filename.c_str()) < 0) {
205 throw Exception{m_sftp};
206 }
207}
208
209File Session::Open(const std::string& filename, int accesstype, mode_t mode) {
210 sftp_file f = sftp_open(m_sftp, filename.c_str(), accesstype, mode);
211 if (!f) {
212 throw Exception{m_sftp};
213 }
214 return File{std::move(f)};
215}