chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

110
vendor/russh-sftp/examples/client.rs vendored Normal file
View File

@@ -0,0 +1,110 @@
use log::{error, info, LevelFilter};
use russh::*;
use russh_sftp::{client::SftpSession, protocol::OpenFlags};
use std::sync::Arc;
use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
struct Client;
impl client::Handler for Client {
type Error = anyhow::Error;
async fn check_server_key(
&mut self,
server_public_key: &russh::keys::PublicKey,
) -> Result<bool, Self::Error> {
info!("check_server_key: {:?}", server_public_key);
Ok(true)
}
async fn data(
&mut self,
channel: ChannelId,
data: &[u8],
_session: &mut client::Session,
) -> Result<(), Self::Error> {
info!("data on channel {:?}: {}", channel, data.len());
Ok(())
}
}
#[tokio::main]
async fn main() {
env_logger::builder()
.filter_level(LevelFilter::Debug)
.init();
let config = russh::client::Config::default();
let sh = Client {};
let mut session = russh::client::connect(Arc::new(config), ("localhost", 22), sh)
.await
.unwrap();
if session
.authenticate_password("root", "password")
.await
.unwrap()
.success()
{
let channel = session.channel_open_session().await.unwrap();
channel.request_subsystem(true, "sftp").await.unwrap();
let sftp = SftpSession::new(channel.into_stream()).await.unwrap();
info!("current path: {:?}", sftp.canonicalize(".").await.unwrap());
// create dir and symlink
let path = "./some_kind_of_dir";
let symlink = "./symlink";
sftp.create_dir(path).await.unwrap();
sftp.symlink(path, symlink).await.unwrap();
info!("dir info: {:?}", sftp.metadata(path).await.unwrap());
info!(
"symlink info: {:?}",
sftp.symlink_metadata(path).await.unwrap()
);
// scanning directory
for entry in sftp.read_dir(".").await.unwrap() {
info!("file in directory: {:?}", entry.file_name());
}
sftp.remove_file(symlink).await.unwrap();
sftp.remove_dir(path).await.unwrap();
// interaction with i/o
let filename = "test_new.txt";
let mut file = sftp
.open_with_flags(
filename,
OpenFlags::CREATE | OpenFlags::TRUNCATE | OpenFlags::WRITE | OpenFlags::READ,
)
.await
.unwrap();
info!("metadata by handle: {:?}", file.metadata().await.unwrap());
file.write_all(b"magic text").await.unwrap();
info!("flush: {:?}", file.flush().await); // or file.sync_all()
info!(
"current cursor position: {:?}",
file.stream_position().await
);
let mut str = String::new();
file.rewind().await.unwrap();
file.read_to_string(&mut str).await.unwrap();
file.rewind().await.unwrap();
info!(
"our magical contents: {}, after rewind: {:?}",
str,
file.stream_position().await
);
file.shutdown().await.unwrap();
sftp.remove_file(filename).await.unwrap();
// should fail because handle was closed
error!("should fail: {:?}", file.read_u8().await);
}
}

201
vendor/russh-sftp/examples/server.rs vendored Normal file
View File

@@ -0,0 +1,201 @@
use log::{error, info, LevelFilter};
use russh::keys::ssh_key::rand_core::OsRng;
use russh::server::{Auth, Msg, Server as _, Session};
use russh::{Channel, ChannelId};
use russh_sftp::protocol::{File, FileAttributes, Handle, Name, Status, StatusCode, Version};
use std::collections::HashMap;
use std::net::SocketAddr;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Mutex;
#[derive(Clone)]
struct Server;
impl russh::server::Server for Server {
type Handler = SshSession;
fn new_client(&mut self, _: Option<SocketAddr>) -> Self::Handler {
SshSession::default()
}
}
struct SshSession {
clients: Arc<Mutex<HashMap<ChannelId, Channel<Msg>>>>,
}
impl Default for SshSession {
fn default() -> Self {
Self {
clients: Arc::new(Mutex::new(HashMap::new())),
}
}
}
impl SshSession {
pub async fn get_channel(&mut self, channel_id: ChannelId) -> Channel<Msg> {
let mut clients = self.clients.lock().await;
clients.remove(&channel_id).unwrap()
}
}
impl russh::server::Handler for SshSession {
type Error = anyhow::Error;
async fn auth_password(&mut self, user: &str, password: &str) -> Result<Auth, Self::Error> {
info!("credentials: {}, {}", user, password);
Ok(Auth::Accept)
}
async fn auth_publickey(
&mut self,
user: &str,
public_key: &russh::keys::PublicKey,
) -> Result<Auth, Self::Error> {
info!("credentials: {}, {:?}", user, public_key);
Ok(Auth::Accept)
}
async fn channel_open_session(
&mut self,
channel: Channel<Msg>,
_session: &mut Session,
) -> Result<bool, Self::Error> {
{
let mut clients = self.clients.lock().await;
clients.insert(channel.id(), channel);
}
Ok(true)
}
async fn channel_eof(
&mut self,
channel: ChannelId,
session: &mut Session,
) -> Result<(), Self::Error> {
// After a client has sent an EOF, indicating that they don't want
// to send more data in this session, the channel can be closed.
session.close(channel)?;
Ok(())
}
async fn subsystem_request(
&mut self,
channel_id: ChannelId,
name: &str,
session: &mut Session,
) -> Result<(), Self::Error> {
info!("subsystem: {}", name);
if name == "sftp" {
let channel = self.get_channel(channel_id).await;
let sftp = SftpSession::default();
session.channel_success(channel_id)?;
russh_sftp::server::run(channel.into_stream(), sftp).await;
} else {
session.channel_failure(channel_id)?;
}
Ok(())
}
}
#[derive(Default)]
struct SftpSession {
version: Option<u32>,
root_dir_read_done: bool,
}
impl russh_sftp::server::Handler for SftpSession {
type Error = StatusCode;
fn unimplemented(&self) -> Self::Error {
StatusCode::OpUnsupported
}
async fn init(
&mut self,
version: u32,
extensions: HashMap<String, String>,
) -> Result<Version, Self::Error> {
if self.version.is_some() {
error!("duplicate SSH_FXP_VERSION packet");
return Err(StatusCode::ConnectionLost);
}
self.version = Some(version);
info!("version: {:?}, extensions: {:?}", self.version, extensions);
Ok(Version::new())
}
async fn close(&mut self, id: u32, _handle: String) -> Result<Status, Self::Error> {
Ok(Status {
id,
status_code: StatusCode::Ok,
error_message: "Ok".to_string(),
language_tag: "en-US".to_string(),
})
}
async fn opendir(&mut self, id: u32, path: String) -> Result<Handle, Self::Error> {
info!("opendir: {}", path);
self.root_dir_read_done = false;
Ok(Handle { id, handle: path })
}
async fn readdir(&mut self, id: u32, handle: String) -> Result<Name, Self::Error> {
info!("readdir handle: {}", handle);
if handle == "/" && !self.root_dir_read_done {
self.root_dir_read_done = true;
return Ok(Name {
id,
files: vec![
File::new("foo", FileAttributes::default()),
File::new("bar", FileAttributes::default()),
],
});
}
// If all files have been sent to the client, respond with an EOF
Err(StatusCode::Eof)
}
async fn realpath(&mut self, id: u32, path: String) -> Result<Name, Self::Error> {
info!("realpath: {}", path);
Ok(Name {
id,
files: vec![File::dummy("/")],
})
}
}
#[tokio::main]
async fn main() {
env_logger::builder()
.filter_level(LevelFilter::Debug)
.init();
let config = russh::server::Config {
auth_rejection_time: Duration::from_secs(3),
auth_rejection_time_initial: Some(Duration::from_secs(0)),
keys: vec![
russh::keys::PrivateKey::random(&mut OsRng, russh::keys::Algorithm::Ed25519).unwrap(),
],
..Default::default()
};
let mut server = Server;
server
.run_on_address(
Arc::new(config),
(
"0.0.0.0",
std::env::var("PORT")
.unwrap_or("22".to_string())
.parse()
.unwrap(),
),
)
.await
.unwrap();
}