melib/imap: support fetching with BODY[] for buggy servers

It appears icloud IMAP servers do not support fetching RFC822 items with
the FETCH command. They reply to the fetch successfully but with the
item missing. To support this clearly non-standard and buggy behavior,
retry one more time by fetching with BODY[] instead if the RFC822 is
missing.

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
pull/453/head
Manos Pitsidianakis 3 months ago
parent 8a74920dc7
commit 197132cca0
No known key found for this signature in database
GPG Key ID: 7729C7707F7E09D0

@ -51,28 +51,30 @@ impl ImapOp {
}
}
impl BackendOp for ImapOp {
fn as_bytes(&self) -> ResultFuture<Vec<u8>> {
let connection = self.connection.clone();
let mailbox_hash = self.mailbox_hash;
let uid = self.uid;
let uid_store = self.uid_store.clone();
Ok(Box::pin(async move {
let exists_in_cache = {
let mut bytes_cache = uid_store.byte_cache.lock()?;
let cache = bytes_cache.entry(uid).or_default();
cache.bytes.is_some()
};
if !exists_in_cache {
impl ImapOp {
async fn fetch(self) -> Result<Vec<u8>> {
let mut response = Vec::with_capacity(8 * 1024);
let mut use_body = false;
let (_uid, _flags, body) = loop {
{
let mut conn = timeout(uid_store.timeout, connection.lock()).await?;
let mut conn = timeout(self.uid_store.timeout, self.connection.lock()).await?;
conn.connect().await?;
conn.examine_mailbox(mailbox_hash, &mut response, false)
conn.examine_mailbox(self.mailbox_hash, &mut response, false)
.await?;
conn.send_command(CommandBody::fetch(
uid,
vec![MessageDataItemName::Flags, MessageDataItemName::Rfc822],
self.uid,
vec![
MessageDataItemName::Flags,
if use_body {
MessageDataItemName::BodyExt {
section: None,
partial: None,
peek: false,
}
} else {
MessageDataItemName::Rfc822
},
],
true,
)?)
.await?;
@ -88,7 +90,7 @@ impl BackendOp for ImapOp {
if results.len() > 1 {
return Err(
Error::new(format!("Invalid/unexpected response: {:?}", response))
.set_summary(format!("Message with UID {} was not found.", uid))
.set_summary(format!("Message with UID {} was not found.", self.uid))
.set_kind(ErrorKind::ProtocolError),
);
}
@ -100,7 +102,7 @@ impl BackendOp for ImapOp {
// without any data or a UID COPY or UID STORE to return an OK without
// performing any operations.
return Err(Error::new("Not found")
.set_summary(format!("Message with UID {} was not found.", uid))
.set_summary(format!("Message with UID {} was not found.", self.uid))
.set_kind(ErrorKind::NotFound));
};
let FetchResponse {
@ -110,30 +112,59 @@ impl BackendOp for ImapOp {
..
} = fetch_response
else {
if !use_body {
use_body = true;
continue;
}
return Err(Error::new("Invalid/unexpected response from server")
.set_summary(format!("Message with UID {} was not found.", uid))
.set_summary(format!("Message with UID {} was not found.", self.uid))
.set_details(format!("Full response: {:?}", fetch_response))
.set_kind(ErrorKind::ProtocolError));
};
if _uid != uid {
break (_uid, _flags, body);
};
if _uid != self.uid {
return Err(Error::new("Invalid/unexpected response from server")
.set_summary(format!("Message with UID {} was not found.", uid))
.set_summary(format!("Message with UID {} was not found.", self.uid))
.set_details(format!(
"Requested UID {} but UID FETCH response was for UID {}. Full \
response: {:?}",
uid,
"Requested UID {} but UID FETCH response was for UID {}. Full response: {:?}",
self.uid,
_uid,
String::from_utf8_lossy(&response)
))
.set_kind(ErrorKind::ProtocolError));
}
let mut bytes_cache = uid_store.byte_cache.lock()?;
let cache = bytes_cache.entry(uid).or_default();
let mut bytes_cache = self.uid_store.byte_cache.lock()?;
let cache = bytes_cache.entry(self.uid).or_default();
if let Some((_flags, _)) = _flags {
cache.flags = Some(_flags);
}
cache.bytes = Some(body.to_vec());
return Ok(body.to_vec());
Ok(body.to_vec())
}
}
impl BackendOp for ImapOp {
fn as_bytes(&self) -> ResultFuture<Vec<u8>> {
let connection = self.connection.clone();
let mailbox_hash = self.mailbox_hash;
let uid = self.uid;
let uid_store = self.uid_store.clone();
Ok(Box::pin(async move {
let exists_in_cache = {
let mut bytes_cache = uid_store.byte_cache.lock()?;
let cache = bytes_cache.entry(uid).or_default();
cache.bytes.is_some()
};
if !exists_in_cache {
return Self {
connection,
mailbox_hash,
uid,
uid_store,
}
.fetch()
.await;
}
let mut bytes_cache = uid_store.byte_cache.lock()?;
let cache = bytes_cache.entry(uid).or_default();

Loading…
Cancel
Save