use std::{
num::NonZeroU32,
ops::{Deref, DerefMut},
};
use anyhow::Result;
use io_imap::{
client::{ImapClient as _, ImapClientStd as Inner},
rfc3501::select::ImapMailboxSelectData,
session::ImapSessionOpenOptions,
types::{
core::{Atom, Vec1},
extensions::enable::CapabilityEnable,
mailbox::Mailbox,
response::Capability,
},
};
use io_sasl::mechanism::Sasl;
use pimalaya_stream::tls::Tls;
use url::Url;
pub struct ImapClient {
inner: Inner,
capabilities: Vec<Capability<'static>>,
selected: Option<String>,
}
impl ImapClient {
pub fn connect(server: &Url, tls: &Tls, starttls: bool, sasl: Option<Sasl>) -> Result<Self> {
let opts = ImapSessionOpenOptions {
starttls,
..Default::default()
};
let (inner, capabilities) = Inner::connect(server, tls, sasl, opts)?;
let mut client = Self {
inner,
capabilities,
selected: None,
};
if client.supports_qresync() {
let condstore = CapabilityEnable::CondStore;
let qresync = CapabilityEnable::from(
Atom::try_from("QRESYNC").expect("`QRESYNC` is a valid IMAP atom"),
);
let caps = Vec1::try_from(vec![condstore, qresync]).expect("two is non-empty");
client.inner.enable(caps)?;
}
Ok(client)
}
pub fn supports_qresync(&self) -> bool {
self.capabilities.contains(&Capability::QResync)
}
pub fn select_delta(
&mut self,
mailbox: Mailbox<'static>,
uid_validity: NonZeroU32,
highest_mod_seq: u64,
) -> Result<ImapMailboxSelectData> {
Ok(self
.inner
.select_qresync(mailbox, uid_validity, highest_mod_seq, &self.capabilities)?)
}
pub fn mark_selected(&mut self, mailbox: &str) {
self.selected = Some(mailbox.to_string());
}
pub fn is_selected(&self, mailbox: &str) -> bool {
self.selected.as_deref() == Some(mailbox)
}
#[allow(dead_code)]
pub fn ping(&mut self) -> Result<()> {
self.inner.noop()?;
Ok(())
}
}
impl Deref for ImapClient {
type Target = Inner;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl DerefMut for ImapClient {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}