use std::net::TcpStream; use std::path::PathBuf; use ssh2::Session; use crate::config::RemoteBackupConfig; use crate::error; use crate::error::AlbatrossError; use crate::remote::{PathLocation, RemoteBackupSite}; /// 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: &RemoteBackupConfig) -> error::Result { let sftp_config = config .sftp .as_ref() .ok_or_else(|| AlbatrossError::RemoteConfigError("SFTP".to_string()))?; let tcp = TcpStream::connect(&sftp_config.server_addr)?; let mut sess = Session::new()?; sess.set_tcp_stream(tcp); sess.handshake().unwrap(); if let Some(password) = &sftp_config.password { sess.userauth_password(&sftp_config.username, password)?; } else if let Some(key) = &sftp_config.private_key { let public_key = sftp_config.public_key.as_deref(); sess.userauth_pubkey_file(&sftp_config.username, public_key, key, None)?; } else { return Err(AlbatrossError::NoSSHAuth); } Ok(Self { session: sess, target_dir: sftp_config.remote_dir.clone(), backups_to_keep: config.backups_to_keep as usize, }) } } 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 } }