diff options
author | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2022-10-22 18:42:07 +0300 |
---|---|---|
committer | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2022-10-22 21:14:53 +0300 |
commit | 803d3414fd73743ff5bfc0fefe5e3d76d88e58cb (patch) | |
tree | a81e60346996a0acde0a6e3051fdaf55add0beea | |
parent | 3697b7d960cc9dbe602fa84f861cea854b600b73 (diff) | |
download | meli-803d3414fd73743ff5bfc0fefe5e3d76d88e58cb.zip |
melib/imap/managesieve: implement some rfc5804 commands
Try with managesieve REPL in src/managesieve.rs:
cargo run --bin managesieve-client ~/.config/meli/config.toml
"accountname"
rfc5804 <https://www.rfc-editor.org/rfc/rfc5804.html>
-rw-r--r-- | Cargo.toml | 6 | ||||
-rw-r--r-- | melib/src/backends/imap/managesieve.rs | 491 | ||||
-rw-r--r-- | melib/src/backends/imap/protocol_parser.rs | 4 | ||||
-rw-r--r-- | src/managesieve.rs | 135 |
4 files changed, 526 insertions, 110 deletions
@@ -21,9 +21,9 @@ path = "src/main.rs" name = "meli" path = "src/lib.rs" -#[[bin]] -#name = "managesieve-meli" -#path = "src/managesieve.rs" +[[bin]] +name = "managesieve-client" +path = "src/managesieve.rs" #[[bin]] #name = "async" diff --git a/melib/src/backends/imap/managesieve.rs b/melib/src/backends/imap/managesieve.rs index 56e3edd9..67c036c6 100644 --- a/melib/src/backends/imap/managesieve.rs +++ b/melib/src/backends/imap/managesieve.rs @@ -21,16 +21,22 @@ use super::{ImapConnection, ImapProtocol, ImapServerConf, UIDStore}; use crate::conf::AccountSettings; +use crate::email::parser::IResult; use crate::error::{MeliError, Result}; use crate::get_conf_val; +use crate::imap::RequiredResponses; use nom::{ - branch::alt, bytes::complete::tag, combinator::map, error::Error as NomError, error::ErrorKind, - multi::separated_list1, sequence::separated_pair, IResult, + branch::alt, bytes::complete::tag, combinator::map, multi::separated_list1, + sequence::separated_pair, }; use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::time::SystemTime; +pub struct ManageSieveConnection { + pub inner: ImapConnection, +} + pub fn managesieve_capabilities(input: &[u8]) -> Result<Vec<(&[u8], &[u8])>> { let (_, ret) = separated_list1( tag(b"\r\n"), @@ -42,26 +48,225 @@ pub fn managesieve_capabilities(input: &[u8]) -> Result<Vec<(&[u8], &[u8])>> { Ok(ret) } -#[test] -fn test_managesieve_capabilities() { - assert_eq!(managesieve_capabilities(b"\"IMPLEMENTATION\" \"Dovecot Pigeonhole\"\r\n\"SIEVE\" \"fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext\"\r\n\"NOTIFY\" \"mailto\"\r\n\"SASL\" \"PLAIN\"\r\n\"STARTTLS\"\r\n\"VERSION\" \"1.0\"\r\n").unwrap(), vec![ - (&b"IMPLEMENTATION"[..],&b"Dovecot Pigeonhole"[..]), - (&b"SIEVE"[..],&b"fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext"[..]), - (&b"NOTIFY"[..],&b"mailto"[..]), - (&b"SASL"[..],&b"PLAIN"[..]), - (&b"STARTTLS"[..], &b""[..]), - (&b"VERSION"[..],&b"1.0"[..])] - - ); +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +pub enum ManageSieveResponse<'a> { + Ok { + code: Option<&'a [u8]>, + message: Option<&'a [u8]>, + }, + NoBye { + code: Option<&'a [u8]>, + message: Option<&'a [u8]>, + }, +} + +mod parser { + use super::*; + use nom::bytes::complete::tag; + pub use nom::bytes::complete::{is_not, tag_no_case}; + use nom::character::complete::crlf; + use nom::combinator::{iterator, map, opt}; + pub use nom::sequence::{delimited, pair, preceded, terminated}; + + pub fn sieve_name(input: &[u8]) -> IResult<&[u8], &[u8]> { + crate::backends::imap::protocol_parser::string_token(input) + } + + // *(sieve-name [SP "ACTIVE"] CRLF) + // response-oknobye + pub fn listscripts(input: &[u8]) -> IResult<&[u8], Vec<(&[u8], bool)>> { + let mut it = iterator( + input, + alt(( + terminated( + map(terminated(sieve_name, tag_no_case(b" ACTIVE")), |r| { + (r, true) + }), + crlf, + ), + terminated(map(sieve_name, |r| (r, false)), crlf), + )), + ); + + let parsed = (&mut it).collect::<Vec<(&[u8], bool)>>(); + let res: IResult<_, _> = it.finish(); + let (rest, _) = res?; + Ok((rest, parsed)) + } + + // response-getscript = (sieve-script CRLF response-ok) / + // response-nobye + pub fn getscript(input: &[u8]) -> IResult<&[u8], &[u8]> { + sieve_name(input) + } + + pub fn response_oknobye(input: &[u8]) -> IResult<&[u8], ManageSieveResponse> { + alt(( + map( + terminated( + pair( + preceded( + tag_no_case(b"ok"), + opt(preceded( + tag(b" "), + delimited(tag(b"("), is_not(")"), tag(b")")), + )), + ), + opt(preceded(tag(b" "), sieve_name)), + ), + crlf, + ), + |(code, message)| ManageSieveResponse::Ok { code, message }, + ), + map( + terminated( + pair( + preceded( + alt((tag_no_case(b"no"), tag_no_case(b"bye"))), + opt(preceded( + tag(b" "), + delimited(tag(b"("), is_not(")"), tag(b")")), + )), + ), + opt(preceded(tag(b" "), sieve_name)), + ), + crlf, + ), + |(code, message)| ManageSieveResponse::NoBye { code, message }, + ), + ))(input) + } + + #[test] + fn test_managesieve_listscripts() { + let input_1 = b"\"summer_script\"\r\n\"vacation_script\"\r\n{13}\r\nclever\"script\r\n\"main_script\" ACTIVE\r\nOK"; + assert_eq!( + terminated(listscripts, tag_no_case(b"OK"))(input_1), + Ok(( + &b""[..], + vec![ + (&b"summer_script"[..], false), + (&b"vacation_script"[..], false), + (&b"clever\"script"[..], false), + (&b"main_script"[..], true) + ] + )) + ); + + let input_2 = b"\"summer_script\"\r\n\"main_script\" active\r\nok"; + assert_eq!( + terminated(listscripts, tag_no_case(b"OK"))(input_2), + Ok(( + &b""[..], + vec![(&b"summer_script"[..], false), (&b"main_script"[..], true)] + )) + ); + let input_3 = b"ok"; + assert_eq!( + terminated(listscripts, tag_no_case(b"OK"))(input_3), + Ok((&b""[..], vec![])) + ); + } + + #[test] + fn test_managesieve_general() { + assert_eq!(managesieve_capabilities(b"\"IMPLEMENTATION\" \"Dovecot Pigeonhole\"\r\n\"SIEVE\" \"fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext\"\r\n\"NOTIFY\" \"mailto\"\r\n\"SASL\" \"PLAIN\"\r\n\"STARTTLS\"\r\n\"VERSION\" \"1.0\"\r\n").unwrap(), vec![ + (&b"IMPLEMENTATION"[..],&b"Dovecot Pigeonhole"[..]), + (&b"SIEVE"[..],&b"fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext"[..]), + (&b"NOTIFY"[..],&b"mailto"[..]), + (&b"SASL"[..],&b"PLAIN"[..]), + (&b"STARTTLS"[..], &b""[..]), + (&b"VERSION"[..],&b"1.0"[..])] + + ); + + let response_ok = b"OK (WARNINGS) \"line 8: server redirect action limit is 2, this redirect might be ignored\"\r\n"; + assert_eq!( + response_oknobye(response_ok), + Ok(( + &b""[..], + ManageSieveResponse::Ok { + code: Some(&b"WARNINGS"[..]), + message: Some(&b"line 8: server redirect action limit is 2, this redirect might be ignored"[..]), + } + )) + ); + let response_ok = b"OK (WARNINGS)\r\n"; + assert_eq!( + response_oknobye(response_ok), + Ok(( + &b""[..], + ManageSieveResponse::Ok { + code: Some(&b"WARNINGS"[..]), + message: None, + } + )) + ); + let response_ok = + b"OK \"line 8: server redirect action limit is 2, this redirect might be ignored\"\r\n"; + assert_eq!( + response_oknobye(response_ok), + Ok(( + &b""[..], + ManageSieveResponse::Ok { + code: None, + message: Some(&b"line 8: server redirect action limit is 2, this redirect might be ignored"[..]), + } + )) + ); + let response_ok = b"Ok\r\n"; + assert_eq!( + response_oknobye(response_ok), + Ok(( + &b""[..], + ManageSieveResponse::Ok { + code: None, + message: None, + } + )) + ); + + let response_nobye = b"No (NONEXISTENT) \"There is no script by that name\"\r\n"; + assert_eq!( + response_oknobye(response_nobye), + Ok(( + &b""[..], + ManageSieveResponse::NoBye { + code: Some(&b"NONEXISTENT"[..]), + message: Some(&b"There is no script by that name"[..]), + } + )) + ); + let response_nobye = b"No (NONEXISTENT) {31}\r\nThere is no script by that name\r\n"; + assert_eq!( + response_oknobye(response_nobye), + Ok(( + &b""[..], + ManageSieveResponse::NoBye { + code: Some(&b"NONEXISTENT"[..]), + message: Some(&b"There is no script by that name"[..]), + } + )) + ); + + let response_nobye = b"No\r\n"; + assert_eq!( + response_oknobye(response_nobye), + Ok(( + &b""[..], + ManageSieveResponse::NoBye { + code: None, + message: None, + } + )) + ); + } } // Return a byte sequence surrounded by "s and decoded if necessary pub fn quoted_raw(input: &[u8]) -> IResult<&[u8], &[u8]> { if input.is_empty() || input[0] != b'"' { - return Err(nom::Err::Error(NomError { - input, - code: ErrorKind::Tag, - })); + return Err(nom::Err::Error((input, "empty").into())); } let mut i = 1; @@ -72,91 +277,199 @@ pub fn quoted_raw(input: &[u8]) -> IResult<&[u8], &[u8]> { i += 1; } - Err(nom::Err::Error(NomError { - input, - code: ErrorKind::Tag, - })) -} - -pub trait ManageSieve { - fn havespace(&mut self) -> Result<()>; - fn putscript(&mut self) -> Result<()>; - - fn listscripts(&mut self) -> Result<()>; - fn setactive(&mut self) -> Result<()>; - - fn getscript(&mut self) -> Result<()>; - - fn deletescript(&mut self) -> Result<()>; - fn renamescript(&mut self) -> Result<()>; + Err(nom::Err::Error((input, "no quotes").into())) } -pub fn new_managesieve_connection( - account_hash: crate::backends::AccountHash, - account_name: String, - s: &AccountSettings, - event_consumer: crate::backends::BackendEventConsumer, -) -> Result<ImapConnection> { - let server_hostname = get_conf_val!(s["server_hostname"])?; - let server_username = get_conf_val!(s["server_username"])?; - let server_password = get_conf_val!(s["server_password"])?; - let server_port = get_conf_val!(s["server_port"], 4190)?; - let danger_accept_invalid_certs: bool = get_conf_val!(s["danger_accept_invalid_certs"], false)?; - let timeout = get_conf_val!(s["timeout"], 16_u64)?; - let timeout = if timeout == 0 { - None - } else { - Some(std::time::Duration::from_secs(timeout)) - }; - let server_conf = ImapServerConf { - server_hostname: server_hostname.to_string(), - server_username: server_username.to_string(), - server_password: server_password.to_string(), - server_port, - use_starttls: true, - use_tls: true, - danger_accept_invalid_certs, - protocol: ImapProtocol::ManageSieve, - timeout, - }; - let uid_store = Arc::new(UIDStore { - is_online: Arc::new(Mutex::new(( - SystemTime::now(), - Err(MeliError::new("Account is uninitialised.")), - ))), - ..UIDStore::new( - account_hash, - Arc::new(account_name), - event_consumer, - server_conf.timeout, - ) - }); - Ok(ImapConnection::new_connection(&server_conf, uid_store)) -} +impl ManageSieveConnection { + pub fn new( + account_hash: crate::backends::AccountHash, + account_name: String, + s: &AccountSettings, + event_consumer: crate::backends::BackendEventConsumer, + ) -> Result<Self> { + let server_hostname = get_conf_val!(s["server_hostname"])?; + let server_username = get_conf_val!(s["server_username"])?; + let server_password = get_conf_val!(s["server_password"])?; + let server_port = get_conf_val!(s["server_port"], 4190)?; + let danger_accept_invalid_certs: bool = + get_conf_val!(s["danger_accept_invalid_certs"], false)?; + let timeout = get_conf_val!(s["timeout"], 16_u64)?; + let timeout = if timeout == 0 { + None + } else { + Some(std::time::Duration::from_secs(timeout)) + }; + let server_conf = ImapServerConf { + server_hostname: server_hostname.to_string(), + server_username: server_username.to_string(), + server_password: server_password.to_string(), + server_port, + use_starttls: true, + use_tls: true, + danger_accept_invalid_certs, + protocol: ImapProtocol::ManageSieve, + timeout, + }; + let uid_store = Arc::new(UIDStore { + is_online: Arc::new(Mutex::new(( + SystemTime::now(), + Err(MeliError::new("Account is uninitialised.")), + ))), + ..UIDStore::new( + account_hash, + Arc::new(account_name), + event_consumer, + server_conf.timeout, + ) + }); + Ok(Self { + inner: ImapConnection::new_connection(&server_conf, uid_store), + }) + } -impl ManageSieve for ImapConnection { - fn havespace(&mut self) -> Result<()> { + pub async fn havespace(&mut self) -> Result<()> { Ok(()) } - fn putscript(&mut self) -> Result<()> { - Ok(()) + + pub async fn putscript(&mut self, script_name: &[u8], script: &[u8]) -> Result<()> { + let mut ret = Vec::new(); + self.inner + .send_literal(format!("Putscript {{{len}+}}\r\n", len = script_name.len()).as_bytes()) + .await?; + self.inner.send_literal(script_name).await?; + self.inner + .send_literal(format!(" {{{len}+}}\r\n", len = script.len()).as_bytes()) + .await?; + self.inner.send_literal(script).await?; + self.inner + .read_response(&mut ret, RequiredResponses::empty()) + .await?; + let (_rest, response) = parser::response_oknobye(&ret)?; + match response { + ManageSieveResponse::Ok { .. } => Ok(()), + ManageSieveResponse::NoBye { code, message } => Err(format!( + "Could not upload script: {} {}", + code.map(|b| String::from_utf8_lossy(b)).unwrap_or_default(), + message + .map(|b| String::from_utf8_lossy(b)) + .unwrap_or_default() + ) + .into()), + } } - fn listscripts(&mut self) -> Result<()> { - Ok(()) + pub async fn listscripts(&mut self) -> Result<Vec<(Vec<u8>, bool)>> { + let mut ret = Vec::new(); + self.inner.send_command(b"Listscripts").await?; + self.inner + .read_response(&mut ret, RequiredResponses::empty()) + .await?; + let (_rest, scripts) = + parser::terminated(parser::listscripts, parser::tag_no_case(b"OK"))(&ret)?; + Ok(scripts + .into_iter() + .map(|(n, a)| (n.to_vec(), a)) + .collect::<Vec<(Vec<u8>, bool)>>()) } - fn setactive(&mut self) -> Result<()> { - Ok(()) + + pub async fn checkscript(&mut self, script: &[u8]) -> Result<()> { + let mut ret = Vec::new(); + self.inner + .send_literal(format!("Checkscript {{{len}+}}\r\n", len = script.len()).as_bytes()) + .await?; + self.inner.send_literal(script).await?; + self.inner + .read_response(&mut ret, RequiredResponses::empty()) + .await?; + let (_rest, response) = parser::response_oknobye(&ret)?; + match response { + ManageSieveResponse::Ok { .. } => Ok(()), + ManageSieveResponse::NoBye { code, message } => Err(format!( + "Checkscript reply: {} {}", + code.map(|b| String::from_utf8_lossy(b)).unwrap_or_default(), + message + .map(|b| String::from_utf8_lossy(b)) + .unwrap_or_default() + ) + .into()), + } } - fn getscript(&mut self) -> Result<()> { - Ok(()) + pub async fn setactive(&mut self, script_name: &[u8]) -> Result<()> { + let mut ret = Vec::new(); + self.inner + .send_literal(format!("Setactive {{{len}+}}\r\n", len = script_name.len()).as_bytes()) + .await?; + self.inner.send_literal(script_name).await?; + self.inner + .read_response(&mut ret, RequiredResponses::empty()) + .await?; + let (_rest, response) = parser::response_oknobye(&ret)?; + match response { + ManageSieveResponse::Ok { .. } => Ok(()), + ManageSieveResponse::NoBye { code, message } => Err(format!( + "Could not set active script: {} {}", + code.map(|b| String::from_utf8_lossy(b)).unwrap_or_default(), + message + .map(|b| String::from_utf8_lossy(b)) + .unwrap_or_default() + ) + .into()), + } } - fn deletescript(&mut self) -> Result<()> { - Ok(()) + pub async fn getscript(&mut self, script_name: &[u8]) -> Result<Vec<u8>> { + let mut ret = Vec::new(); + self.inner + .send_literal(format!("Getscript {{{len}+}}\r\n", len = script_name.len()).as_bytes()) + .await?; + self.inner.send_literal(script_name).await?; + self.inner + .read_response(&mut ret, RequiredResponses::empty()) + .await?; + if let Ok((_, ManageSieveResponse::NoBye { code, message })) = + parser::response_oknobye(&ret) + { + return Err(format!( + "Could not set active script: {} {}", + code.map(|b| String::from_utf8_lossy(b)).unwrap_or_default(), + message + .map(|b| String::from_utf8_lossy(b)) + .unwrap_or_default() + ) + .into()); + } + let (_rest, script) = + parser::terminated(parser::getscript, parser::tag_no_case(b"OK"))(&ret)?; + Ok(script.to_vec()) } - fn renamescript(&mut self) -> Result<()> { + + pub async fn deletescript(&mut self, script_name: &[u8]) -> Result<()> { + let mut ret = Vec::new(); + self.inner + .send_literal( + format!("Deletescript {{{len}+}}\r\n", len = script_name.len()).as_bytes(), + ) + .await?; + self.inner.send_literal(script_name).await?; + self.inner + .read_response(&mut ret, RequiredResponses::empty()) + .await?; + let (_rest, response) = parser::response_oknobye(&ret)?; + match response { + ManageSieveResponse::Ok { .. } => Ok(()), + ManageSieveResponse::NoBye { code, message } => Err(format!( + "Could not delete script: {} {}", + code.map(|b| String::from_utf8_lossy(b)).unwrap_or_default(), + message + .map(|b| String::from_utf8_lossy(b)) + .unwrap_or_default() + ) + .into()), + } + } + + pub async fn renamescript(&mut self) -> Result<()> { Ok(()) } } diff --git a/melib/src/backends/imap/protocol_parser.rs b/melib/src/backends/imap/protocol_parser.rs index d546078e..b58e1b06 100644 --- a/melib/src/backends/imap/protocol_parser.rs +++ b/melib/src/backends/imap/protocol_parser.rs @@ -1626,12 +1626,12 @@ pub fn mailbox_token(input: &'_ [u8]) -> IResult<&'_ [u8], std::borrow::Cow<'_, } // astring = 1*ASTRING-CHAR / string -fn astring_token(input: &[u8]) -> IResult<&[u8], &[u8]> { +pub fn astring_token(input: &[u8]) -> IResult<&[u8], &[u8]> { alt((string_token, astring_char))(input) } // string = quoted / literal -fn string_token(input: &[u8]) -> IResult<&[u8], &[u8]> { +pub fn string_token(input: &[u8]) -> IResult<&[u8], &[u8]> { if let Ok((r, o)) = literal(input) { return Ok((r, o)); } diff --git a/src/managesieve.rs b/src/managesieve.rs index f26353ee..340978a9 100644 --- a/src/managesieve.rs +++ b/src/managesieve.rs @@ -24,17 +24,15 @@ extern crate melib; use melib::*; use std::collections::VecDeque; -extern crate xdg_utils; #[macro_use] extern crate serde_derive; extern crate linkify; -extern crate uuid; extern crate serde_json; extern crate smallvec; extern crate termion; -use melib::backends::imap::managesieve::new_managesieve_connection; +use melib::backends::imap::managesieve::ManageSieveConnection; use melib::Result; #[macro_use] @@ -64,7 +62,7 @@ pub mod sqlite3; pub mod jobs; pub mod mailcap; -pub mod plugins; +//pub mod plugins; use futures::executor::block_on; @@ -84,10 +82,7 @@ fn main() -> Result<()> { std::process::exit(1); } - let (config_path, account_name) = ( - std::mem::replace(&mut args[0], String::new()), - std::mem::replace(&mut args[1], String::new()), - ); + let (config_path, account_name) = (std::mem::take(&mut args[0]), std::mem::take(&mut args[1])); std::env::set_var("MELI_CONFIG", config_path); let settings = conf::Settings::new()?; if !settings.accounts.contains_key(&account_name) { @@ -102,12 +97,47 @@ fn main() -> Result<()> { ); std::process::exit(1); } - let mut conn = new_managesieve_connection(&settings.accounts[&account_name].account)?; - block_on(conn.connect())?; - let mut res = String::with_capacity(8 * 1024); + let account = &settings.accounts[&account_name].account; + let mut conn = ManageSieveConnection::new( + 0, + account_name.clone(), + account, + melib::backends::BackendEventConsumer::new(std::sync::Arc::new(|_, _| {})), + )?; + block_on(conn.inner.connect())?; let mut input = String::new(); - println!("managesieve shell: use 'logout'"); + const AVAILABLE_COMMANDS: &[&str] = &[ + "help", + "logout", + "listscripts", + "checkscript", + "putscript", + "setactive", + "getscript", + "deletescript", + ]; + const COMMANDS_HELP: &[&str] = &[ + "help", + "logout", + "listscripts and whether they are active", + "paste a script to check for validity without uploading it", + "upload a script", + "set a script as active", + "get a script by its name", + "delete a script by its name", + ]; + println!("managesieve shell: use 'help' for available commands"); + enum PrevCmd { + None, + Checkscript, + PutscriptName, + PutscriptString(String), + SetActiveName, + GetScriptName, + } + use PrevCmd::*; + let mut prev_cmd: PrevCmd = None; loop { use std::io; use std::io::Write; @@ -116,12 +146,85 @@ fn main() -> Result<()> { io::stdout().flush().unwrap(); match io::stdin().read_line(&mut input) { Ok(_) => { - if input.trim().eq_ignore_ascii_case("logout") { + let input = input.trim(); + if input.eq_ignore_ascii_case("logout") { break; } - block_on(conn.send_command(input.as_bytes()))?; - block_on(conn.read_lines(&mut res, String::new()))?; - println!("out: {}", res.trim()); + if input.eq_ignore_ascii_case("help") { + println!("available commands: [{}]", AVAILABLE_COMMANDS.join(", ")); + continue; + } + if input.len() >= "help ".len() + && input[0.."help ".len()].eq_ignore_ascii_case("help ") + { + if let Some(i) = AVAILABLE_COMMANDS + .iter() + .position(|cmd| cmd.eq_ignore_ascii_case(&input["help ".len()..])) + { + println!("{}", COMMANDS_HELP[i]); + } else { + println!("invalid command `{}`", &input["help ".len()..]); + } + continue; + } + if input.eq_ignore_ascii_case("listscripts") { + let scripts = block_on(conn.listscripts())?; + println!("Got {} scripts:", scripts.len()); + for (script, active) in scripts { + println!( + "{}active: {}", + if active { "" } else { "in" }, + String::from_utf8_lossy(&script) + ); + } + } else if input.eq_ignore_ascii_case("checkscript") { + prev_cmd = Checkscript; + println!("insert file path of script"); + } else if input.eq_ignore_ascii_case("putscript") { + prev_cmd = PutscriptName; + println!("Insert script name"); + } else if input.eq_ignore_ascii_case("setactive") { + prev_cmd = SetActiveName; + } else if input.eq_ignore_ascii_case("getscript") { + prev_cmd = GetScriptName; + } else if input.eq_ignore_ascii_case("deletescript") { + println!("unimplemented `{}`", input); + } else { + match prev_cmd { + None => println!("invalid command `{}`", input), + Checkscript => { + let content = std::fs::read_to_string(&input).unwrap(); + let result = block_on(conn.checkscript(content.as_bytes())); + println!("Got {:?}", result); + prev_cmd = None; + } + PutscriptName => { + prev_cmd = PutscriptString(input.to_string()); + println!("insert file path of script"); + } + PutscriptString(name) => { + prev_cmd = None; + let content = std::fs::read_to_string(&input).unwrap(); + let result = + block_on(conn.putscript(name.as_bytes(), content.as_bytes())); + println!("Got {:?}", result); + } + SetActiveName => { + prev_cmd = None; + let result = block_on(conn.setactive(input.as_bytes())); + println!("Got {:?}", result); + } + GetScriptName => { + prev_cmd = None; + let result = block_on(conn.getscript(input.as_bytes())); + println!("Got {:?}", result); + } + } + } + + //block_on(conn.send_command(input.as_bytes()))?; + //block_on(conn.read_lines(&mut res, String::new()))?; + //println!("out: {}", res.trim()); } Err(error) => println!("error: {}", error), } |