diff --git a/src/commands.rs b/src/commands.rs index d009b6e..575fea6 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -134,6 +134,10 @@ pub enum Command { /// The (regular) file to delete. path: String, }, + Rmd { + /// The (regular) directory to delete. + path: String, + }, /// The `QUIT` command Quit, /// The `MKD` command @@ -351,6 +355,15 @@ impl Command { let path = String::from_utf8_lossy(&path).to_string(); Command::Dele { path } } + b"RMD" | b"rmd" => { + let path = parse_to_eol(cmd_params)?; + if path.is_empty() { + return Err(ParseErrorKind::InvalidCommand)?; + } + + let path = String::from_utf8_lossy(&path).to_string(); + Command::Rmd { path } + } b"QUIT" | b"quit" => { let params = parse_to_eol(cmd_params)?; if !params.is_empty() { @@ -993,6 +1006,25 @@ mod tests { ); } + #[test] + fn parse_rmd() { + let input = "RMD\r\n"; + assert_eq!( + Command::parse(input), + Err(ParseError { + inner: Context::new(ParseErrorKind::InvalidCommand) + }) + ); + + let input = "RMD some_directory\r\n"; + assert_eq!( + Command::parse(input), + Ok(Command::Rmd { + path: "some_directory".into() + }) + ); + } + #[test] fn parse_quit() { let input = "QUIT\r\n"; diff --git a/src/server.rs b/src/server.rs index 1904bf6..dd2a3d2 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1025,6 +1025,44 @@ where ); Ok(Reply::none()) } + Command::Rmd { path } => { + ensure_authenticated!(); + let session = session.lock()?; + let storage = Arc::clone(&session.storage); + let tx_success = tx.clone(); + let tx_fail = tx.clone(); + tokio::spawn( + storage + .rmd(path) + .map_err(|_| { + std::io::Error::new( + ErrorKind::Other, + "Failed to delete directory", + ) + }) + .and_then(|_| { + tx_success.send(InternalMsg::DelSuccess).map_err(|_| { + std::io::Error::new( + ErrorKind::Other, + "Failed to send 'DelSuccess' to data channel", + ) + }) + }) + .or_else(|_| { + tx_fail.send(InternalMsg::DelFail).map_err(|_| { + std::io::Error::new( + ErrorKind::Other, + "Failed to send 'DelFail' to data channel", + ) + }) + }) + .map(|_| ()) + .map_err(|e| { + warn!("Failed to delete directory: {}", e); + }), + ); + Ok(Reply::none()) + } Command::Quit => { let tx = tx.clone(); spawn!(tx.send(InternalMsg::Quit)); diff --git a/src/storage.rs b/src/storage.rs index 3e903f7..5595e26 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -203,6 +203,9 @@ pub trait StorageBackend { /// Delete the given file. fn del>(&self, path: P) -> Box + Send>; + /// Delete the given directory. + fn rmd>(&self, path: P) -> Box + Send>; + /// Create the given directory. fn mkd>(&self, path: P) -> Box + Send>; @@ -357,6 +360,14 @@ impl StorageBackend for Filesystem { Box::new(tokio::fs::remove_file(full_path).map_err(|_| Error::IOError)) } + fn rmd>(&self, path: P) -> Box + Send> { + let full_path = match self.full_path(path) { + Ok(path) => path, + Err(e) => return Box::new(future::err(e)), + }; + Box::new(tokio::fs::remove_dir(full_path).map_err(|_| Error::IOError)) + } + fn mkd>(&self, path: P) -> Box + Send> { let full_path = match self.full_path(path) { Ok(path) => path,