use std::net::TcpStream; use std::path::PathBuf; use ssh2::Session; use crate::error; use crate::error::AlbatrossError; use crate::remote::{PathLocation, RemoteBackupSite}; use crate::config::remote::SFTPConfig; /// SFTP Remote Site pub struct SFTPBackup { /// SSH Session session: Session, /// Remote target directory target_dir: PathBuf, /// Number of backups to keep backups_to_keep: usize, } impl SFTPBackup { /// New SFTPBackup pub fn new(config: &SFTPConfig, backups_to_keep: usize) -> error::Result { let tcp = TcpStream::connect(&config.server_addr)?; let mut sess = Session::new()?; sess.set_tcp_stream(tcp); sess.handshake().unwrap(); if let Some(password) = &config.password { sess.userauth_password(&config.username, password)?; } else if let Some(key) = &config.private_key { let public_key = config.public_key.as_deref(); sess.userauth_pubkey_file(&config.username, public_key, key, None)?; } else { return Err(AlbatrossError::NoSSHAuth); } Ok(Self { session: sess, target_dir: config.remote_dir.clone(), backups_to_keep, }) } } impl RemoteBackupSite for SFTPBackup { type FileType = PathLocation; fn backup_to_remote(&mut self, file: PathBuf) -> error::Result<()> { let remote_path = self.target_dir.join(file.file_name().unwrap()); let mut local_file = std::fs::File::open(&file)?; let sftp = self.session.sftp()?; let mut remote_file = sftp.create(&remote_path)?; std::io::copy(&mut local_file, &mut remote_file)?; Ok(()) } fn get_backups(&mut self) -> error::Result> { let files = self.session.sftp()?.readdir(&self.target_dir)?; Ok(files .into_iter() .filter_map(|(file, _)| Self::FileType::new(file)) .collect()) } fn remove_backup(&mut self, backup: Self::FileType) -> error::Result<()> { Ok(self.session.sftp()?.unlink(&*backup.location)?) } fn backups_to_keep(&self) -> usize { self.backups_to_keep } }