This commit is contained in:
Michael Zhang 2021-02-24 06:43:50 -06:00
parent 5b3bf5dd3c
commit 523e6eaf7a
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
8 changed files with 68 additions and 14 deletions

View file

@ -22,7 +22,6 @@ format-bytes = "0.2.0"
futures = "0.3.13" futures = "0.3.13"
inotify = { version = "0.9.2", features = ["stream"] } inotify = { version = "0.9.2", features = ["stream"] }
log = "0.4.14" log = "0.4.14"
panorama-imap = { path = "imap", version = "0" }
parking_lot = "0.11.1" parking_lot = "0.11.1"
pgp = "0.7.1" pgp = "0.7.1"
pin-project = "1.0.5" pin-project = "1.0.5"
@ -37,5 +36,10 @@ toml = "0.5.8"
webpki-roots = "0.21.0" webpki-roots = "0.21.0"
xdg = "2.2.0" xdg = "2.2.0"
[dependencies.panorama-imap]
path = "imap"
version = "0"
features = ["rfc2177-idle"]
[features] [features]
clippy = [] clippy = []

View file

@ -28,3 +28,7 @@ webpki-roots = "0.21.0"
[dev-dependencies] [dev-dependencies]
assert_matches = "1.3" assert_matches = "1.3"
[features]
default = ["rfc2177-idle"]
rfc2177-idle = []

View file

@ -100,10 +100,10 @@ where
} }
let cmd_str = format!("{}{} {}\r\n", TAG_PREFIX, id, cmd); let cmd_str = format!("{}{} {}\r\n", TAG_PREFIX, id, cmd);
debug!("[{}] writing to socket: {:?}", id, cmd_str); // debug!("[{}] writing to socket: {:?}", id, cmd_str);
self.conn.write_all(cmd_str.as_bytes()).await?; self.conn.write_all(cmd_str.as_bytes()).await?;
self.conn.flush().await?; self.conn.flush().await?;
debug!("[{}] written.", id); // debug!("[{}] written.", id);
let resp = ExecWaiter(self, id, false).await; let resp = ExecWaiter(self, id, false).await;
// let resp = { // let resp = {
@ -252,7 +252,7 @@ async fn listen<C>(
where where
C: AsyncRead + Unpin, C: AsyncRead + Unpin,
{ {
debug!("amogus"); // debug!("amogus");
let mut reader = BufReader::new(conn); let mut reader = BufReader::new(conn);
let mut greeting = Some(greeting); let mut greeting = Some(greeting);

View file

@ -166,4 +166,13 @@ impl ClientAuthenticated {
debug!("select response: {:?}", resp); debug!("select response: {:?}", resp);
Ok(()) Ok(())
} }
/// Runs the SELECT command
#[cfg(feature = "rfc2177-idle")]
pub async fn idle(&mut self) -> Result<()> {
let cmd = Command::Idle;
let resp = self.execute(cmd).await?;
debug!("idle response: {:?}", resp);
Ok(())
}
} }

View file

@ -5,9 +5,20 @@ use std::fmt;
pub enum Command { pub enum Command {
Capability, Capability,
Starttls, Starttls,
Login { username: String, password: String }, Login {
Select { mailbox: String }, username: String,
List { reference: String, mailbox: String }, password: String,
},
Select {
mailbox: String,
},
List {
reference: String,
mailbox: String,
},
#[cfg(feature = "rfc2177-idle")]
Idle,
} }
impl fmt::Display for Command { impl fmt::Display for Command {
@ -19,6 +30,9 @@ impl fmt::Display for Command {
Login { username, password } => write!(f, "LOGIN {} {}", username, password), Login { username, password } => write!(f, "LOGIN {} {}", username, password),
Select { mailbox } => write!(f, "SELECT {}", mailbox), Select { mailbox } => write!(f, "SELECT {}", mailbox),
List { reference, mailbox } => write!(f, "LIST {:?} {:?}", reference, mailbox), List { reference, mailbox } => write!(f, "LIST {:?} {:?}", reference, mailbox),
#[cfg(feature = "rfc2177-idle")]
Idle => write!(f, "IDLE"),
} }
} }
} }

View file

@ -15,13 +15,15 @@ use crate::response::*;
#[grammar = "parser/rfc3501.pest"] #[grammar = "parser/rfc3501.pest"]
struct Rfc3501; struct Rfc3501;
pub fn parse_capability(s: impl AsRef<str>) -> Result<Capability, Error<Rule>> { pub type ParseResult<T, E = Error<Rule>> = Result<T, E>;
pub fn parse_capability(s: impl AsRef<str>) -> ParseResult<Capability> {
let mut pairs = Rfc3501::parse(Rule::capability, s.as_ref())?; let mut pairs = Rfc3501::parse(Rule::capability, s.as_ref())?;
let pair = pairs.next().unwrap(); let pair = pairs.next().unwrap();
Ok(build_capability(pair)) Ok(build_capability(pair))
} }
pub fn parse_response(s: impl AsRef<str>) -> Result<Response, Error<Rule>> { pub fn parse_response(s: impl AsRef<str>) -> ParseResult<Response> {
let mut pairs = Rfc3501::parse(Rule::response, s.as_ref())?; let mut pairs = Rfc3501::parse(Rule::response, s.as_ref())?;
let pair = pairs.next().unwrap(); let pair = pairs.next().unwrap();
Ok(build_response(pair)) Ok(build_response(pair))
@ -90,10 +92,31 @@ fn build_response(pair: Pair<Rule>) -> Response {
_ => unreachable!("{:#?}", pair), _ => unreachable!("{:#?}", pair),
} }
} }
Rule::continue_req => {
let (code, s) = build_resp_text(unwrap1(pair));
Response::Continue {
code,
information: Some(s),
}
}
_ => unreachable!("{:#?}", pair), _ => unreachable!("{:#?}", pair),
} }
} }
fn build_resp_text(pair: Pair<Rule>) -> (Option<ResponseCode>, String) {
assert!(matches!(pair.as_rule(), Rule::resp_text));
let mut pairs = pair.into_inner();
let mut pair = pairs.next().unwrap();
let mut resp_code = None;
if let Rule::resp_text_code = pair.as_rule() {
resp_code = build_resp_text_code(pair);
pair = pairs.next().unwrap();
}
assert!(matches!(pair.as_rule(), Rule::text));
let s = pair.as_str().to_owned();
(resp_code, s)
}
fn build_msg_att(pair: Pair<Rule>) -> AttributeValue { fn build_msg_att(pair: Pair<Rule>) -> AttributeValue {
if !matches!(pair.as_rule(), Rule::msg_att_dyn_or_stat) { if !matches!(pair.as_rule(), Rule::msg_att_dyn_or_stat) {
unreachable!("{:#?}", pair); unreachable!("{:#?}", pair);
@ -153,7 +176,7 @@ fn build_resp_cond_state(pair: Pair<Rule>) -> (Status, Option<ResponseCode>, Opt
let pairs = pair.into_inner(); let pairs = pair.into_inner();
for pair in pairs { for pair in pairs {
match pair.as_rule() { match pair.as_rule() {
Rule::resp_text_code => code = build_resp_code(pair), Rule::resp_text_code => code = build_resp_text_code(pair),
Rule::text => information = Some(pair.as_str().to_owned()), Rule::text => information = Some(pair.as_str().to_owned()),
_ => unreachable!("{:#?}", pair), _ => unreachable!("{:#?}", pair),
} }
@ -162,7 +185,7 @@ fn build_resp_cond_state(pair: Pair<Rule>) -> (Status, Option<ResponseCode>, Opt
(status, code, information) (status, code, information)
} }
fn build_resp_code(pair: Pair<Rule>) -> Option<ResponseCode> { fn build_resp_text_code(pair: Pair<Rule>) -> Option<ResponseCode> {
if !matches!(pair.as_rule(), Rule::resp_text_code) { if !matches!(pair.as_rule(), Rule::resp_text_code) {
unreachable!("{:#?}", pair); unreachable!("{:#?}", pair);
} }

View file

@ -135,11 +135,11 @@ zone = @{ ("+" | "-") ~ digit{4} }
// core rules from https://tools.ietf.org/html/rfc2234#section-6.1 // core rules from https://tools.ietf.org/html/rfc2234#section-6.1
alpha = @{ '\x41'..'\x5a' | '\x61'..'\x7a' } alpha = @{ '\x41'..'\x5a' | '\x61'..'\x7a' }
char = @{ '\x01'..'\x7f' } char = @{ '\x01'..'\x7f' }
cr = @{ "\x0d" } cr = _{ "\x0d" }
crlf = _{ cr ~ lf } crlf = _{ cr ~ lf }
ctl = @{ '\x00'..'\x1f' | "\x7f" } ctl = @{ '\x00'..'\x1f' | "\x7f" }
digit = @{ '\x30'..'\x39' } digit = @{ '\x30'..'\x39' }
dquote = @{ "\"" } dquote = @{ "\"" }
lf = @{ "\x0a" } lf = _{ "\x0a" }
sp = _{ " " } sp = _{ " " }

View file

@ -110,7 +110,7 @@ async fn imap_main(acct: MailAccountConfig) -> Result<()> {
debug!("listing all emails..."); debug!("listing all emails...");
let folder_tree = authed.list().await?; let folder_tree = authed.list().await?;
tokio::time::sleep(std::time::Duration::from_secs(60)).await; let idle = authed.idle().await?;
debug!("heartbeat"); debug!("heartbeat");
} }
} }