Fetch ENVELOPE works \o/
This commit is contained in:
parent
de71f4b4ed
commit
b71d967889
4 changed files with 110 additions and 13 deletions
|
@ -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)> {
|
||||
|
|
|
@ -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())),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in a new issue