ouais
This commit is contained in:
parent
5b3bf5dd3c
commit
523e6eaf7a
8 changed files with 68 additions and 14 deletions
|
@ -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 = []
|
||||||
|
|
|
@ -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 = []
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = _{ " " }
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue