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,
|
attributes: &MacroOrFetchAttributes,
|
||||||
uid: &bool,
|
uid: &bool,
|
||||||
) -> Result<(Response, flow::Transition)> {
|
) -> Result<(Response, flow::Transition)> {
|
||||||
let resp = self.mailbox.fetch(sequence_set, attributes, uid).await?;
|
match self.mailbox.fetch(sequence_set, attributes, uid).await {
|
||||||
|
Ok(resp) => Ok((
|
||||||
Ok((
|
Response::ok("FETCH completed")?.with_body(resp),
|
||||||
Response::ok("FETCH completed")?.with_body(resp),
|
flow::Transition::None,
|
||||||
flow::Transition::None,
|
)),
|
||||||
))
|
Err(e) => Ok((Response::no(&e.to_string())?, flow::Transition::None)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn noop(self) -> Result<(Response, flow::Transition)> {
|
pub async fn noop(self) -> Result<(Response, flow::Transition)> {
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
|
use std::borrow::Borrow;
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::{anyhow, bail, Error, Result};
|
use anyhow::{anyhow, bail, Error, Result};
|
||||||
use boitalettres::proto::res::body::Data as Body;
|
use boitalettres::proto::res::body::Data as Body;
|
||||||
use futures::stream::{FuturesOrdered, StreamExt};
|
use futures::stream::{FuturesOrdered, StreamExt};
|
||||||
|
use imap_codec::types::address::Address;
|
||||||
use imap_codec::types::core::{Atom, IString, NString};
|
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::fetch_attributes::{FetchAttribute, MacroOrFetchAttributes};
|
||||||
use imap_codec::types::flag::Flag;
|
use imap_codec::types::flag::Flag;
|
||||||
use imap_codec::types::response::{Code, Data, MessageAttribute, Status};
|
use imap_codec::types::response::{Code, Data, MessageAttribute, Status};
|
||||||
|
@ -210,7 +213,7 @@ impl MailboxView {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut ret = vec![];
|
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 mut attributes = vec![MessageAttribute::Uid(uid)];
|
||||||
|
|
||||||
let (_uid2, flags) = self
|
let (_uid2, flags) = self
|
||||||
|
@ -219,6 +222,14 @@ impl MailboxView {
|
||||||
.get(&uuid)
|
.get(&uuid)
|
||||||
.ok_or_else(|| anyhow!("Mail not in uidindex table: {}", 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() {
|
for attr in fetch_attrs.iter() {
|
||||||
match attr {
|
match attr {
|
||||||
FetchAttribute::Uid => (),
|
FetchAttribute::Uid => (),
|
||||||
|
@ -235,7 +246,9 @@ impl MailboxView {
|
||||||
IString::Literal(meta.headers.clone().try_into().unwrap()),
|
IString::Literal(meta.headers.clone().try_into().unwrap()),
|
||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
|
FetchAttribute::Envelope => {
|
||||||
|
attributes.push(MessageAttribute::Envelope(message_envelope(&parsed)))
|
||||||
|
}
|
||||||
// TODO
|
// TODO
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -247,6 +260,7 @@ impl MailboxView {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tracing::info!("Fetch result: {:?}", ret);
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,3 +368,70 @@ fn string_to_flag(f: &str) -> Option<Flag> {
|
||||||
None => None,
|
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 anyhow::{anyhow, bail, Result};
|
||||||
use k2v_client::K2vClient;
|
use k2v_client::K2vClient;
|
||||||
use k2v_client::{BatchReadOp, Filter, K2vValue};
|
use k2v_client::{BatchReadOp, Filter, K2vValue};
|
||||||
use rusoto_s3::{
|
use rusoto_s3::{DeleteObjectRequest, GetObjectRequest, PutObjectRequest, S3Client, S3};
|
||||||
DeleteObjectRequest, GetObjectRequest, PutObjectRequest, S3Client, S3,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
@ -44,7 +42,7 @@ impl Mailbox {
|
||||||
|
|
||||||
/// Sync data with backing store
|
/// Sync data with backing store
|
||||||
pub async fn sync(&self) -> Result<()> {
|
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
|
/// 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
|
/// Copy an email from an other Mailbox to this mailbox
|
||||||
/// (use this when possible, as it allows for a certain number of storage optimizations)
|
/// (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!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,6 +102,11 @@ struct MailboxInternal {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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>> {
|
async fn fetch_meta(&self, ids: &[UniqueIdent]) -> Result<Vec<MailMeta>> {
|
||||||
let ids = ids.iter().map(|x| x.to_string()).collect::<Vec<_>>();
|
let ids = ids.iter().map(|x| x.to_string()).collect::<Vec<_>>();
|
||||||
let ops = ids
|
let ops = ids
|
||||||
|
|
|
@ -118,6 +118,12 @@ async fn main() -> Result<()> {
|
||||||
std::env::set_var("RUST_LOG", "main=info,mailrage=info,k2v_client=info")
|
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();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
Loading…
Reference in a new issue