aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2022-06-29 20:00:38 +0200
committerAlex Auvolat <alex@adnab.me>2022-06-29 20:00:38 +0200
commitb71d9678892814a62d422af11eb91e61943836a2 (patch)
treebbfcb8964fa87361595dfb89d9c174bf24458b0a /src
parentde71f4b4edcd66e68a79ce15800a7eab0f6ad72c (diff)
downloadaerogramme-b71d9678892814a62d422af11eb91e61943836a2.tar.gz
aerogramme-b71d9678892814a62d422af11eb91e61943836a2.zip
Fetch ENVELOPE works \o/
Diffstat (limited to 'src')
-rw-r--r--src/imap/command/selected.rs13
-rw-r--r--src/imap/mailbox_view.rs85
-rw-r--r--src/mail/mailbox.rs19
-rw-r--r--src/main.rs6
4 files changed, 110 insertions, 13 deletions
diff --git a/src/imap/command/selected.rs b/src/imap/command/selected.rs
index e10eab6..b3a2ffd 100644
--- a/src/imap/command/selected.rs
+++ b/src/imap/command/selected.rs
@@ -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)> {
diff --git a/src/imap/mailbox_view.rs b/src/imap/mailbox_view.rs
index 6b24c8a..9a956b6 100644
--- a/src/imap/mailbox_view.rs
+++ b/src/imap/mailbox_view.rs
@@ -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())),
+ )
+}
diff --git a/src/mail/mailbox.rs b/src/mail/mailbox.rs
index f559d5c..bb1ca7a 100644
--- a/src/mail/mailbox.rs
+++ b/src/mail/mailbox.rs
@@ -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
diff --git a/src/main.rs b/src/main.rs
index 5d139b6..6744157 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -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();