diff --git a/imap/src/client/inner.rs b/imap/src/client/inner.rs index ed79375..9f73a39 100644 --- a/imap/src/client/inner.rs +++ b/imap/src/client/inner.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll, Waker}; @@ -20,11 +20,11 @@ use tokio_rustls::{ use crate::command::Command; use crate::response::{Capability, Response, ResponseCode}; -use crate::types::Status; +use crate::types::{Capability as Capability_, Status}; use super::ClientConfig; -pub type CapsLock = Arc>>>; +pub type CapsLock = Arc>>>; pub type ResultMap = Arc, Option)>>>; pub type GreetingState = Arc)>>; pub const TAG_PREFIX: &str = "panorama"; @@ -130,6 +130,10 @@ where /// Attempts to upgrade this connection using STARTTLS pub async fn upgrade(mut self) -> Result>> { // TODO: make sure STARTTLS is in the capability list + if !self.has_capability("STARTTLS").await? { + bail!("server doesn't support this capability"); + } + // first, send the STARTTLS command let resp = self.execute(Command::Starttls).await?; debug!("server response to starttls: {:?}", resp); @@ -154,6 +158,34 @@ where Ok(Client::new(stream, self.config)) } + + /// Check if this client has a particular capability + pub async fn has_capability(&self, cap: impl AsRef) -> Result { + let cap = cap.as_ref().to_owned(); + debug!("checking for the capability: {:?}", cap); + + let cap_bytes = cap.as_bytes(); + debug!("cap_bytes {:?}", cap_bytes); + let (_, cap) = match crate::oldparser::rfc3501::capability(cap_bytes) { + Ok(v) => v, + Err(err) => { + error!("ERROR PARSING {:?} {} {:?}", cap, err, err); + use std::error::Error; + let bt = err.backtrace().unwrap(); + error!("{}", bt); + std::process::exit(1); + } + }; + let cap = Capability::from(cap); + + let caps = &*self.caps.read(); + // TODO: refresh caps + + let caps = caps.as_ref().unwrap(); + let result = caps.contains(&cap); + debug!("cap result: {:?}", result); + Ok(result) + } } pub struct GreetingWaiter(GreetingState); @@ -226,7 +258,7 @@ where } debug!("got a new line {:?}", next_line); - let (_, resp) = match crate::parser::parse_response(next_line.as_bytes()) { + let (_, resp) = match crate::oldparser::parse_response(next_line.as_bytes()) { Ok(v) => v, Err(err) => { debug!("shiet: {:?}", err); @@ -246,6 +278,7 @@ where let resp = Response::from(resp); debug!("resp: {:?}", resp); match &resp { + // capabilities list Response::Capabilities(new_caps) | Response::Data { status: Status::Ok, @@ -253,10 +286,18 @@ where .. } => { let caps = &mut *caps.write(); - *caps = Some(new_caps.clone()); + *caps = Some(new_caps.iter().cloned().collect()); debug!("new caps: {:?}", caps); } + // bye + Response::Data { + status: Status::Bye, + .. + } => { + bail!("disconnected"); + } + Response::Done { tag, .. } => { let tag_str = &tag.0; if tag_str.starts_with(TAG_PREFIX) { diff --git a/imap/src/lib.rs b/imap/src/lib.rs index f613dd8..b7a5439 100644 --- a/imap/src/lib.rs +++ b/imap/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(backtrace)] + #[macro_use] extern crate anyhow; #[macro_use] @@ -10,9 +12,9 @@ extern crate log; pub mod builders; pub mod client; pub mod command; -pub mod parser; +pub mod oldparser; pub mod response; pub mod types; -pub use crate::parser::ParseResult; +pub use crate::oldparser::ParseResult; pub use crate::types::*; diff --git a/imap/src/parser/rfc3501/body.rs b/imap/src/parser/rfc3501/body.rs index 1bdcf7d..d9d5f15 100644 --- a/imap/src/parser/rfc3501/body.rs +++ b/imap/src/parser/rfc3501/body.rs @@ -8,7 +8,7 @@ use nom::{ IResult, }; -use crate::{parser::core::*, types::*}; +use crate::{oldparser::core::*, types::*}; pub fn section_part(i: &[u8]) -> IResult<&[u8], Vec> { let (i, (part, mut rest)) = tuple((number, many0(preceded(char('.'), number))))(i)?; diff --git a/imap/src/parser/rfc3501/body_structure.rs b/imap/src/parser/rfc3501/body_structure.rs index 72a48d5..c81ab4e 100644 --- a/imap/src/parser/rfc3501/body_structure.rs +++ b/imap/src/parser/rfc3501/body_structure.rs @@ -9,7 +9,7 @@ use nom::{ }; use crate::{ - parser::{core::*, rfc3501::envelope}, + oldparser::{core::*, rfc3501::envelope}, types::*, }; diff --git a/imap/src/parser/rfc3501/mod.rs b/imap/src/parser/rfc3501/mod.rs index 460ae16..c125582 100644 --- a/imap/src/parser/rfc3501/mod.rs +++ b/imap/src/parser/rfc3501/mod.rs @@ -17,7 +17,7 @@ use nom::{ }; use crate::{ - parser::{ + oldparser::{ core::*, rfc3501::body::*, rfc3501::body_structure::*, rfc4315, rfc4551, rfc5161, rfc5464, rfc7162, }, @@ -181,7 +181,7 @@ fn resp_text_code(i: &[u8]) -> IResult<&[u8], ResponseCode> { )(i) } -fn capability(i: &[u8]) -> IResult<&[u8], Capability> { +pub fn capability(i: &[u8]) -> IResult<&[u8], Capability> { alt(( map(tag_no_case(b"IMAP4rev1"), |_| Capability::Imap4rev1), map(preceded(tag_no_case(b"AUTH="), atom), Capability::Auth), diff --git a/imap/src/parser/rfc4315.rs b/imap/src/parser/rfc4315.rs index 9b2b13f..ac01876 100644 --- a/imap/src/parser/rfc4315.rs +++ b/imap/src/parser/rfc4315.rs @@ -13,7 +13,7 @@ use nom::{ IResult, }; -use crate::parser::core::number; +use crate::oldparser::core::number; use crate::types::*; /// Extends resp-text-code as follows: diff --git a/imap/src/parser/rfc4551.rs b/imap/src/parser/rfc4551.rs index 443062d..5c05e24 100644 --- a/imap/src/parser/rfc4551.rs +++ b/imap/src/parser/rfc4551.rs @@ -8,7 +8,7 @@ use nom::{bytes::streaming::tag_no_case, sequence::tuple, IResult}; use crate::{ - parser::core::{number_64, paren_delimited}, + oldparser::core::{number_64, paren_delimited}, types::*, }; diff --git a/imap/src/parser/rfc5161.rs b/imap/src/parser/rfc5161.rs index 3a1f7c4..09be547 100644 --- a/imap/src/parser/rfc5161.rs +++ b/imap/src/parser/rfc5161.rs @@ -13,7 +13,7 @@ use nom::{ IResult, }; -use crate::parser::core::atom; +use crate::oldparser::core::atom; use crate::types::*; // The ENABLED response lists capabilities that were enabled in response @@ -23,7 +23,7 @@ pub(crate) fn resp_enabled(i: &[u8]) -> IResult<&[u8], Response> { map(enabled_data, Response::Capabilities)(i) } -fn enabled_data(i: &[u8]) -> IResult<&[u8], Vec> { +pub fn enabled_data(i: &[u8]) -> IResult<&[u8], Vec> { let (i, (_, capabilities)) = tuple(( tag_no_case("ENABLED"), many0(preceded(char(' '), capability)), @@ -31,6 +31,6 @@ fn enabled_data(i: &[u8]) -> IResult<&[u8], Vec> { Ok((i, capabilities)) } -fn capability(i: &[u8]) -> IResult<&[u8], Capability> { +pub fn capability(i: &[u8]) -> IResult<&[u8], Capability> { map(atom, Capability::Atom)(i) } diff --git a/imap/src/parser/rfc5464.rs b/imap/src/parser/rfc5464.rs index 6b2ba3b..e50ff9e 100644 --- a/imap/src/parser/rfc5464.rs +++ b/imap/src/parser/rfc5464.rs @@ -13,7 +13,7 @@ use nom::{ IResult, }; -use crate::{parser::core::*, types::*}; +use crate::{oldparser::core::*, types::*}; fn is_entry_component_char(c: u8) -> bool { c < 0x80 && c > 0x19 && c != b'*' && c != b'%' && c != b'/' diff --git a/imap/src/parser/rfc7162.rs b/imap/src/parser/rfc7162.rs index afa7ae6..e23fb1a 100644 --- a/imap/src/parser/rfc7162.rs +++ b/imap/src/parser/rfc7162.rs @@ -10,7 +10,7 @@ use nom::{ IResult, }; -use crate::parser::core::sequence_set; +use crate::oldparser::core::sequence_set; use crate::types::*; // The VANISHED response reports that the specified UIDs have been diff --git a/imap/src/response/mod.rs b/imap/src/response/mod.rs index 74589a3..ba009f1 100644 --- a/imap/src/response/mod.rs +++ b/imap/src/response/mod.rs @@ -70,7 +70,7 @@ impl<'a> From> for Response { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub enum Capability { Imap4rev1, Auth(String), diff --git a/imap/src/types.rs b/imap/src/types.rs index 63fe5a7..7366b6c 100644 --- a/imap/src/types.rs +++ b/imap/src/types.rs @@ -40,7 +40,7 @@ pub enum Response<'a> { impl<'a> Response<'a> { pub fn from_bytes(buf: &'a [u8]) -> crate::ParseResult { - crate::parser::parse_response(buf) + crate::oldparser::parse_response(buf) } } diff --git a/src/ui/mod.rs b/src/ui/mod.rs index a2bb49e..27915e8 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -28,6 +28,10 @@ pub struct Rect(u16, u16, u16, u16); /// UI entrypoint. pub async fn run_ui(mut w: impl Write + Debug, exit: ExitSender) -> Result<()> { + loop { + tokio::time::sleep(Duration::from_secs(4000)).await; + } + execute!(w, cursor::Hide, terminal::EnterAlternateScreen)?; terminal::enable_raw_mode()?;