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>
pull/161/head
Manos Pitsidianakis 2022-10-22 18:42:07 +03:00
parent 3697b7d960
commit 803d3414fd
4 changed files with 527 additions and 111 deletions

View File

@ -21,9 +21,9 @@ path = "src/main.rs"
name = "meli" name = "meli"
path = "src/lib.rs" path = "src/lib.rs"
#[[bin]] [[bin]]
#name = "managesieve-meli" name = "managesieve-client"
#path = "src/managesieve.rs" path = "src/managesieve.rs"
#[[bin]] #[[bin]]
#name = "async" #name = "async"

View File

@ -21,16 +21,22 @@
use super::{ImapConnection, ImapProtocol, ImapServerConf, UIDStore}; use super::{ImapConnection, ImapProtocol, ImapServerConf, UIDStore};
use crate::conf::AccountSettings; use crate::conf::AccountSettings;
use crate::email::parser::IResult;
use crate::error::{MeliError, Result}; use crate::error::{MeliError, Result};
use crate::get_conf_val; use crate::get_conf_val;
use crate::imap::RequiredResponses;
use nom::{ use nom::{
branch::alt, bytes::complete::tag, combinator::map, error::Error as NomError, error::ErrorKind, branch::alt, bytes::complete::tag, combinator::map, multi::separated_list1,
multi::separated_list1, sequence::separated_pair, IResult, sequence::separated_pair,
}; };
use std::str::FromStr; use std::str::FromStr;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::SystemTime; use std::time::SystemTime;
pub struct ManageSieveConnection {
pub inner: ImapConnection,
}
pub fn managesieve_capabilities(input: &[u8]) -> Result<Vec<(&[u8], &[u8])>> { pub fn managesieve_capabilities(input: &[u8]) -> Result<Vec<(&[u8], &[u8])>> {
let (_, ret) = separated_list1( let (_, ret) = separated_list1(
tag(b"\r\n"), tag(b"\r\n"),
@ -42,26 +48,225 @@ pub fn managesieve_capabilities(input: &[u8]) -> Result<Vec<(&[u8], &[u8])>> {
Ok(ret) Ok(ret)
} }
#[test] #[derive(PartialEq, Eq, Debug, Copy, Clone)]
fn test_managesieve_capabilities() { pub enum ManageSieveResponse<'a> {
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![ Ok {
(&b"IMPLEMENTATION"[..],&b"Dovecot Pigeonhole"[..]), code: Option<&'a [u8]>,
(&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"[..]), message: Option<&'a [u8]>,
(&b"NOTIFY"[..],&b"mailto"[..]), },
(&b"SASL"[..],&b"PLAIN"[..]), NoBye {
(&b"STARTTLS"[..], &b""[..]), code: Option<&'a [u8]>,
(&b"VERSION"[..],&b"1.0"[..])] 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 // Return a byte sequence surrounded by "s and decoded if necessary
pub fn quoted_raw(input: &[u8]) -> IResult<&[u8], &[u8]> { pub fn quoted_raw(input: &[u8]) -> IResult<&[u8], &[u8]> {
if input.is_empty() || input[0] != b'"' { if input.is_empty() || input[0] != b'"' {
return Err(nom::Err::Error(NomError { return Err(nom::Err::Error((input, "empty").into()));
input,
code: ErrorKind::Tag,
}));
} }
let mut i = 1; let mut i = 1;
@ -72,91 +277,199 @@ pub fn quoted_raw(input: &[u8]) -> IResult<&[u8], &[u8]> {
i += 1; i += 1;
} }
Err(nom::Err::Error(NomError { Err(nom::Err::Error((input, "no quotes").into()))
input,
code: ErrorKind::Tag,
}))
} }
pub trait ManageSieve { impl ManageSieveConnection {
fn havespace(&mut self) -> Result<()>; pub fn new(
fn putscript(&mut self) -> Result<()>; account_hash: crate::backends::AccountHash,
account_name: String,
fn listscripts(&mut self) -> Result<()>; s: &AccountSettings,
fn setactive(&mut self) -> Result<()>; event_consumer: crate::backends::BackendEventConsumer,
) -> Result<Self> {
fn getscript(&mut self) -> Result<()>; let server_hostname = get_conf_val!(s["server_hostname"])?;
let server_username = get_conf_val!(s["server_username"])?;
fn deletescript(&mut self) -> Result<()>; let server_password = get_conf_val!(s["server_password"])?;
fn renamescript(&mut self) -> Result<()>; 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)?;
pub fn new_managesieve_connection( let timeout = get_conf_val!(s["timeout"], 16_u64)?;
account_hash: crate::backends::AccountHash, let timeout = if timeout == 0 {
account_name: String, None
s: &AccountSettings, } else {
event_consumer: crate::backends::BackendEventConsumer, Some(std::time::Duration::from_secs(timeout))
) -> Result<ImapConnection> { };
let server_hostname = get_conf_val!(s["server_hostname"])?; let server_conf = ImapServerConf {
let server_username = get_conf_val!(s["server_username"])?; server_hostname: server_hostname.to_string(),
let server_password = get_conf_val!(s["server_password"])?; server_username: server_username.to_string(),
let server_port = get_conf_val!(s["server_port"], 4190)?; server_password: server_password.to_string(),
let danger_accept_invalid_certs: bool = get_conf_val!(s["danger_accept_invalid_certs"], false)?; server_port,
let timeout = get_conf_val!(s["timeout"], 16_u64)?; use_starttls: true,
let timeout = if timeout == 0 { use_tls: true,
None danger_accept_invalid_certs,
} else { protocol: ImapProtocol::ManageSieve,
Some(std::time::Duration::from_secs(timeout)) timeout,
}; };
let server_conf = ImapServerConf { let uid_store = Arc::new(UIDStore {
server_hostname: server_hostname.to_string(), is_online: Arc::new(Mutex::new((
server_username: server_username.to_string(), SystemTime::now(),
server_password: server_password.to_string(), Err(MeliError::new("Account is uninitialised.")),
server_port, ))),
use_starttls: true, ..UIDStore::new(
use_tls: true, account_hash,
danger_accept_invalid_certs, Arc::new(account_name),
protocol: ImapProtocol::ManageSieve, event_consumer,
timeout, server_conf.timeout,
}; )
let uid_store = Arc::new(UIDStore { });
is_online: Arc::new(Mutex::new(( Ok(Self {
SystemTime::now(), inner: ImapConnection::new_connection(&server_conf, uid_store),
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 ManageSieve for ImapConnection {
fn havespace(&mut self) -> Result<()> {
Ok(())
} }
fn putscript(&mut self) -> Result<()> {
pub async fn havespace(&mut self) -> Result<()> {
Ok(()) Ok(())
} }
fn listscripts(&mut self) -> Result<()> { pub async fn putscript(&mut self, script_name: &[u8], script: &[u8]) -> Result<()> {
Ok(()) let mut ret = Vec::new();
} self.inner
fn setactive(&mut self) -> Result<()> { .send_literal(format!("Putscript {{{len}+}}\r\n", len = script_name.len()).as_bytes())
Ok(()) .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 getscript(&mut self) -> Result<()> { pub async fn listscripts(&mut self) -> Result<Vec<(Vec<u8>, bool)>> {
Ok(()) 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 deletescript(&mut self) -> Result<()> { pub async fn checkscript(&mut self, script: &[u8]) -> Result<()> {
Ok(()) 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 renamescript(&mut self) -> Result<()> {
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()),
}
}
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())
}
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(()) Ok(())
} }
} }

View File

@ -1626,12 +1626,12 @@ pub fn mailbox_token(input: &'_ [u8]) -> IResult<&'_ [u8], std::borrow::Cow<'_,
} }
// astring = 1*ASTRING-CHAR / string // 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) alt((string_token, astring_char))(input)
} }
// string = quoted / literal // 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) { if let Ok((r, o)) = literal(input) {
return Ok((r, o)); return Ok((r, o));
} }

View File

@ -24,17 +24,15 @@ extern crate melib;
use melib::*; use melib::*;
use std::collections::VecDeque; use std::collections::VecDeque;
extern crate xdg_utils;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
extern crate linkify; extern crate linkify;
extern crate uuid;
extern crate serde_json; extern crate serde_json;
extern crate smallvec; extern crate smallvec;
extern crate termion; extern crate termion;
use melib::backends::imap::managesieve::new_managesieve_connection; use melib::backends::imap::managesieve::ManageSieveConnection;
use melib::Result; use melib::Result;
#[macro_use] #[macro_use]
@ -64,7 +62,7 @@ pub mod sqlite3;
pub mod jobs; pub mod jobs;
pub mod mailcap; pub mod mailcap;
pub mod plugins; //pub mod plugins;
use futures::executor::block_on; use futures::executor::block_on;
@ -84,10 +82,7 @@ fn main() -> Result<()> {
std::process::exit(1); std::process::exit(1);
} }
let (config_path, account_name) = ( let (config_path, account_name) = (std::mem::take(&mut args[0]), std::mem::take(&mut args[1]));
std::mem::replace(&mut args[0], String::new()),
std::mem::replace(&mut args[1], String::new()),
);
std::env::set_var("MELI_CONFIG", config_path); std::env::set_var("MELI_CONFIG", config_path);
let settings = conf::Settings::new()?; let settings = conf::Settings::new()?;
if !settings.accounts.contains_key(&account_name) { if !settings.accounts.contains_key(&account_name) {
@ -102,12 +97,47 @@ fn main() -> Result<()> {
); );
std::process::exit(1); std::process::exit(1);
} }
let mut conn = new_managesieve_connection(&settings.accounts[&account_name].account)?; let account = &settings.accounts[&account_name].account;
block_on(conn.connect())?; let mut conn = ManageSieveConnection::new(
let mut res = String::with_capacity(8 * 1024); 0,
account_name.clone(),
account,
melib::backends::BackendEventConsumer::new(std::sync::Arc::new(|_, _| {})),
)?;
block_on(conn.inner.connect())?;
let mut input = String::new(); 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 { loop {
use std::io; use std::io;
use std::io::Write; use std::io::Write;
@ -116,12 +146,85 @@ fn main() -> Result<()> {
io::stdout().flush().unwrap(); io::stdout().flush().unwrap();
match io::stdin().read_line(&mut input) { match io::stdin().read_line(&mut input) {
Ok(_) => { Ok(_) => {
if input.trim().eq_ignore_ascii_case("logout") { let input = input.trim();
if input.eq_ignore_ascii_case("logout") {
break; break;
} }
block_on(conn.send_command(input.as_bytes()))?; if input.eq_ignore_ascii_case("help") {
block_on(conn.read_lines(&mut res, String::new()))?; println!("available commands: [{}]", AVAILABLE_COMMANDS.join(", "));
println!("out: {}", res.trim()); 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), Err(error) => println!("error: {}", error),
} }