Fetch ENVELOPE works \o/

This commit is contained in:
Alex 2022-06-29 20:00:38 +02:00
parent de71f4b4ed
commit b71d967889
Signed by: lx
GPG Key ID: 0E496D15096376BE
4 changed files with 110 additions and 13 deletions

View File

@ -46,12 +46,13 @@ impl<'a> SelectedContext<'a> {
attributes: &MacroOrFetchAttributes,
uid: &bool,
) -> Result<(Response, flow::Transition)> {
let resp = self.mailbox.fetch(sequence_set, attributes, uid).await?;
Ok((
Response::ok("FETCH completed")?.with_body(resp),
flow::Transition::None,
))
match self.mailbox.fetch(sequence_set, attributes, uid).await {
Ok(resp) => Ok((
Response::ok("FETCH completed")?.with_body(resp),
flow::Transition::None,
)),
Err(e) => Ok((Response::no(&e.to_string())?, flow::Transition::None)),
}
}
pub async fn noop(self) -> Result<(Response, flow::Transition)> {

View File

@ -1,10 +1,13 @@
use std::borrow::Borrow;
use std::num::NonZeroU32;
use std::sync::Arc;
use anyhow::{anyhow, bail, Error, Result};
use boitalettres::proto::res::body::Data as Body;
use futures::stream::{FuturesOrdered, StreamExt};
use imap_codec::types::address::Address;
use imap_codec::types::core::{Atom, IString, NString};
use imap_codec::types::envelope::Envelope;
use imap_codec::types::fetch_attributes::{FetchAttribute, MacroOrFetchAttributes};
use imap_codec::types::flag::Flag;
use imap_codec::types::response::{Code, Data, MessageAttribute, Status};
@ -210,7 +213,7 @@ impl MailboxView {
};
let mut ret = vec![];
for (i, uid, uuid, meta, _body) in mails {
for (i, uid, uuid, meta, body) in mails {
let mut attributes = vec![MessageAttribute::Uid(uid)];
let (_uid2, flags) = self
@ -219,6 +222,14 @@ impl MailboxView {
.get(&uuid)
.ok_or_else(|| anyhow!("Mail not in uidindex table: {}", uuid))?;
let parsed = match &body {
Some(m) => {
mail_parser::Message::parse(m).ok_or_else(|| anyhow!("Invalid mail body"))?
}
None => mail_parser::Message::parse(&meta.headers)
.ok_or_else(|| anyhow!("Invalid mail headers"))?,
};
for attr in fetch_attrs.iter() {
match attr {
FetchAttribute::Uid => (),
@ -235,7 +246,9 @@ impl MailboxView {
IString::Literal(meta.headers.clone().try_into().unwrap()),
))))
}
FetchAttribute::Envelope => {
attributes.push(MessageAttribute::Envelope(message_envelope(&parsed)))
}
// TODO
_ => (),
}
@ -247,6 +260,7 @@ impl MailboxView {
}));
}
tracing::info!("Fetch result: {:?}", ret);
Ok(ret)
}
@ -354,3 +368,70 @@ fn string_to_flag(f: &str) -> Option<Flag> {
None => None,
}
}
fn message_envelope(msg: &mail_parser::Message<'_>) -> Envelope {
Envelope {
date: NString(
msg.get_date()
.map(|d| IString::try_from(d.to_iso8601()).unwrap()),
),
subject: NString(
msg.get_subject()
.map(|d| IString::try_from(d.to_string()).unwrap()),
),
from: convert_addresses(msg.get_from()),
sender: convert_addresses(msg.get_sender()),
reply_to: convert_addresses(msg.get_reply_to()),
to: convert_addresses(msg.get_to()),
cc: convert_addresses(msg.get_cc()),
bcc: convert_addresses(msg.get_bcc()),
in_reply_to: NString(None), // TODO
message_id: NString(
msg.get_message_id()
.map(|d| IString::try_from(d.to_string()).unwrap()),
),
}
}
fn convert_addresses(a: &mail_parser::HeaderValue<'_>) -> Vec<Address> {
match a {
mail_parser::HeaderValue::Address(a) => vec![convert_address(a)],
mail_parser::HeaderValue::AddressList(a) => {
let mut ret = vec![];
for aa in a {
ret.push(convert_address(aa));
}
ret
}
mail_parser::HeaderValue::Empty => vec![],
mail_parser::HeaderValue::Collection(c) => {
let mut ret = vec![];
for cc in c.iter() {
ret.extend(convert_addresses(cc).into_iter());
}
ret
}
_ => panic!("Invalid address header"),
}
}
fn convert_address(a: &mail_parser::Addr<'_>) -> Address {
let (user, host) = match &a.address {
None => (None, None),
Some(x) => match x.split_once('@') {
Some((u, h)) => (Some(u.to_string()), Some(h.to_string())),
None => (Some(x.to_string()), None),
},
};
Address::new(
NString(
a.name
.as_ref()
.map(|x| IString::try_from(x.to_string()).unwrap()),
),
NString(None),
NString(user.map(|x| IString::try_from(x).unwrap())),
NString(host.map(|x| IString::try_from(x).unwrap())),
)
}

View File

@ -1,9 +1,7 @@
use anyhow::{anyhow, bail, Result};
use k2v_client::K2vClient;
use k2v_client::{BatchReadOp, Filter, K2vValue};
use rusoto_s3::{
DeleteObjectRequest, GetObjectRequest, PutObjectRequest, S3Client, S3,
};
use rusoto_s3::{DeleteObjectRequest, GetObjectRequest, PutObjectRequest, S3Client, S3};
use serde::{Deserialize, Serialize};
use tokio::io::AsyncReadExt;
use tokio::sync::RwLock;
@ -44,7 +42,7 @@ impl Mailbox {
/// Sync data with backing store
pub async fn sync(&self) -> Result<()> {
self.mbox.write().await.uid_index.sync().await
self.mbox.write().await.sync().await
}
/// Get a clone of the current UID Index of this mailbox
@ -60,7 +58,13 @@ impl Mailbox {
/// Copy an email from an other Mailbox to this mailbox
/// (use this when possible, as it allows for a certain number of storage optimizations)
pub async fn copy(&self, _from: &Mailbox, _uid: ImapUid) -> Result<()> {
pub async fn copy_from(&self, _from: &Mailbox, _uuid: UniqueIdent) -> Result<()> {
unimplemented!()
}
/// Move an email from an other Mailbox to this mailbox
/// (use this when possible, as it allows for a certain number of storage optimizations)
pub async fn move_from(&self, _from: &Mailbox, _uuid: UniqueIdent) -> Result<()> {
unimplemented!()
}
@ -98,6 +102,11 @@ struct MailboxInternal {
}
impl MailboxInternal {
async fn sync(&mut self) -> Result<()> {
self.uid_index.sync().await?;
Ok(())
}
async fn fetch_meta(&self, ids: &[UniqueIdent]) -> Result<Vec<MailMeta>> {
let ids = ids.iter().map(|x| x.to_string()).collect::<Vec<_>>();
let ops = ids

View File

@ -118,6 +118,12 @@ async fn main() -> Result<()> {
std::env::set_var("RUST_LOG", "main=info,mailrage=info,k2v_client=info")
}
// Abort on panic (same behavior as in Go)
std::panic::set_hook(Box::new(|panic_info| {
tracing::error!("{}", panic_info.to_string());
std::process::abort();
}));
tracing_subscriber::fmt::init();
let args = Args::parse();