Rename MailIdent to UniqueIdent and use those to identify mailboxes
This commit is contained in:
parent
b95028f89e
commit
d737e33b5a
7 changed files with 76 additions and 74 deletions
|
@ -8,7 +8,7 @@ use imap_codec::types::flag::Flag;
|
||||||
use imap_codec::types::mailbox::{ListMailbox, Mailbox as MailboxCodec};
|
use imap_codec::types::mailbox::{ListMailbox, Mailbox as MailboxCodec};
|
||||||
use imap_codec::types::response::{Code, Data, Status};
|
use imap_codec::types::response::{Code, Data, Status};
|
||||||
|
|
||||||
use crate::mail::mailbox::{Mailbox, Summary};
|
use crate::mail::mailbox::Mailbox;
|
||||||
use crate::mail::uidindex::UidIndex;
|
use crate::mail::uidindex::UidIndex;
|
||||||
|
|
||||||
const DEFAULT_FLAGS: [Flag; 5] = [
|
const DEFAULT_FLAGS: [Flag; 5] = [
|
||||||
|
@ -35,6 +35,7 @@ impl MailboxView {
|
||||||
/// Creates a new IMAP view into a mailbox.
|
/// Creates a new IMAP view into a mailbox.
|
||||||
/// Generates the necessary IMAP messages so that the client
|
/// Generates the necessary IMAP messages so that the client
|
||||||
/// has a satisfactory summary of the current mailbox's state.
|
/// has a satisfactory summary of the current mailbox's state.
|
||||||
|
/// These are the messages that are sent in response to a SELECT command.
|
||||||
pub async fn new(mailbox: Arc<Mailbox>) -> Result<(Self, Vec<Body>)> {
|
pub async fn new(mailbox: Arc<Mailbox>) -> Result<(Self, Vec<Body>)> {
|
||||||
let state = mailbox.current_uid_index().await;
|
let state = mailbox.current_uid_index().await;
|
||||||
|
|
||||||
|
@ -140,7 +141,11 @@ impl MailboxView {
|
||||||
})
|
})
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect();
|
.collect();
|
||||||
flags.extend_from_slice(&DEFAULT_FLAGS);
|
for f in DEFAULT_FLAGS.iter() {
|
||||||
|
if !flags.contains(f) {
|
||||||
|
flags.push(f.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
let mut ret = vec![Body::Data(Data::Flags(flags.clone()))];
|
let mut ret = vec![Body::Data(Data::Flags(flags.clone()))];
|
||||||
|
|
||||||
flags.push(Flag::Permanent);
|
flags.push(Flag::Permanent);
|
||||||
|
|
|
@ -20,7 +20,7 @@ use smtp_server::{reply, Config, ConnectionMetadata, Decision, MailMetadata};
|
||||||
use crate::config::*;
|
use crate::config::*;
|
||||||
use crate::cryptoblob::*;
|
use crate::cryptoblob::*;
|
||||||
use crate::login::*;
|
use crate::login::*;
|
||||||
use crate::mail::mail_ident::*;
|
use crate::mail::unique_ident::*;
|
||||||
|
|
||||||
pub struct LmtpServer {
|
pub struct LmtpServer {
|
||||||
bind_addr: SocketAddr,
|
bind_addr: SocketAddr,
|
||||||
|
|
|
@ -8,53 +8,40 @@ use tokio::sync::RwLock;
|
||||||
use crate::bayou::Bayou;
|
use crate::bayou::Bayou;
|
||||||
use crate::cryptoblob::Key;
|
use crate::cryptoblob::Key;
|
||||||
use crate::login::Credentials;
|
use crate::login::Credentials;
|
||||||
use crate::mail::mail_ident::*;
|
|
||||||
use crate::mail::uidindex::*;
|
use crate::mail::uidindex::*;
|
||||||
|
use crate::mail::unique_ident::*;
|
||||||
use crate::mail::IMF;
|
use crate::mail::IMF;
|
||||||
|
|
||||||
pub struct Summary {
|
pub struct Mailbox {
|
||||||
pub validity: ImapUidvalidity,
|
id: UniqueIdent,
|
||||||
pub next: ImapUid,
|
mbox: RwLock<MailboxInternal>,
|
||||||
pub exists: u32,
|
|
||||||
pub recent: u32,
|
|
||||||
pub flags: Vec<String>,
|
|
||||||
pub unseen: Option<ImapUid>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for Summary {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"uidvalidity: {}, uidnext: {}, exists: {}",
|
|
||||||
self.validity, self.next, self.exists
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Mailbox(RwLock<MailboxInternal>);
|
|
||||||
|
|
||||||
impl Mailbox {
|
impl Mailbox {
|
||||||
pub(super) async fn open(creds: &Credentials, name: &str) -> Result<Self> {
|
pub(super) async fn open(creds: &Credentials, id: UniqueIdent) -> Result<Self> {
|
||||||
let index_path = format!("index/{}", name);
|
let index_path = format!("index/{}", id);
|
||||||
let mail_path = format!("mail/{}", name);
|
let mail_path = format!("mail/{}", id);
|
||||||
|
|
||||||
let mut uid_index = Bayou::<UidIndex>::new(creds, index_path)?;
|
let mut uid_index = Bayou::<UidIndex>::new(creds, index_path)?;
|
||||||
uid_index.sync().await?;
|
uid_index.sync().await?;
|
||||||
|
|
||||||
Ok(Self(RwLock::new(MailboxInternal {
|
let mbox = RwLock::new(MailboxInternal {
|
||||||
|
id,
|
||||||
bucket: creds.bucket().to_string(),
|
bucket: creds.bucket().to_string(),
|
||||||
key: creds.keys.master.clone(),
|
encryption_key: creds.keys.master.clone(),
|
||||||
k2v: creds.k2v_client()?,
|
k2v: creds.k2v_client()?,
|
||||||
s3: creds.s3_client()?,
|
s3: creds.s3_client()?,
|
||||||
uid_index,
|
uid_index,
|
||||||
mail_path,
|
mail_path,
|
||||||
})))
|
});
|
||||||
|
|
||||||
|
Ok(Self { id, mbox })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a clone of the current UID Index of this mailbox
|
/// Get a clone of the current UID Index of this mailbox
|
||||||
/// (cloning is cheap so don't hesitate to use this)
|
/// (cloning is cheap so don't hesitate to use this)
|
||||||
pub async fn current_uid_index(&self) -> UidIndex {
|
pub async fn current_uid_index(&self) -> UidIndex {
|
||||||
self.0.read().await.uid_index.state().clone()
|
self.mbox.read().await.uid_index.state().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert an email in the mailbox
|
/// Insert an email in the mailbox
|
||||||
|
@ -91,14 +78,15 @@ impl Mailbox {
|
||||||
// Non standard but common flags:
|
// Non standard but common flags:
|
||||||
// https://www.iana.org/assignments/imap-jmap-keywords/imap-jmap-keywords.xhtml
|
// https://www.iana.org/assignments/imap-jmap-keywords/imap-jmap-keywords.xhtml
|
||||||
struct MailboxInternal {
|
struct MailboxInternal {
|
||||||
|
id: UniqueIdent,
|
||||||
bucket: String,
|
bucket: String,
|
||||||
key: Key,
|
mail_path: String,
|
||||||
|
encryption_key: Key,
|
||||||
|
|
||||||
k2v: K2vClient,
|
k2v: K2vClient,
|
||||||
s3: S3Client,
|
s3: S3Client,
|
||||||
|
|
||||||
uid_index: Bayou<UidIndex>,
|
uid_index: Bayou<UidIndex>,
|
||||||
mail_path: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MailboxInternal {
|
impl MailboxInternal {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
pub mod mail_ident;
|
|
||||||
pub mod mailbox;
|
pub mod mailbox;
|
||||||
pub mod uidindex;
|
pub mod uidindex;
|
||||||
|
pub mod unique_ident;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
@ -12,8 +12,8 @@ use rusoto_s3::S3Client;
|
||||||
use crate::bayou::Bayou;
|
use crate::bayou::Bayou;
|
||||||
use crate::cryptoblob::Key;
|
use crate::cryptoblob::Key;
|
||||||
use crate::login::Credentials;
|
use crate::login::Credentials;
|
||||||
use crate::mail::mail_ident::*;
|
|
||||||
use crate::mail::uidindex::*;
|
use crate::mail::uidindex::*;
|
||||||
|
use crate::mail::unique_ident::*;
|
||||||
|
|
||||||
// Internet Message Format
|
// Internet Message Format
|
||||||
// aka RFC 822 - RFC 2822 - RFC 5322
|
// aka RFC 822 - RFC 2822 - RFC 5322
|
||||||
|
|
|
@ -4,7 +4,7 @@ use im::{HashMap, OrdMap, OrdSet};
|
||||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
|
||||||
use crate::bayou::*;
|
use crate::bayou::*;
|
||||||
use crate::mail::mail_ident::MailIdent;
|
use crate::mail::unique_ident::UniqueIdent;
|
||||||
|
|
||||||
pub type ImapUid = NonZeroU32;
|
pub type ImapUid = NonZeroU32;
|
||||||
pub type ImapUidvalidity = NonZeroU32;
|
pub type ImapUidvalidity = NonZeroU32;
|
||||||
|
@ -18,10 +18,10 @@ pub type Flag = String;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct UidIndex {
|
pub struct UidIndex {
|
||||||
// Source of trust
|
// Source of trust
|
||||||
pub table: OrdMap<MailIdent, (ImapUid, Vec<Flag>)>,
|
pub table: OrdMap<UniqueIdent, (ImapUid, Vec<Flag>)>,
|
||||||
|
|
||||||
// Indexes optimized for queries
|
// Indexes optimized for queries
|
||||||
pub idx_by_uid: OrdMap<ImapUid, MailIdent>,
|
pub idx_by_uid: OrdMap<ImapUid, UniqueIdent>,
|
||||||
pub idx_by_flag: FlagIndex,
|
pub idx_by_flag: FlagIndex,
|
||||||
|
|
||||||
// Counters
|
// Counters
|
||||||
|
@ -32,36 +32,36 @@ pub struct UidIndex {
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Serialize, Deserialize, Debug)]
|
||||||
pub enum UidIndexOp {
|
pub enum UidIndexOp {
|
||||||
MailAdd(MailIdent, ImapUid, Vec<Flag>),
|
MailAdd(UniqueIdent, ImapUid, Vec<Flag>),
|
||||||
MailDel(MailIdent),
|
MailDel(UniqueIdent),
|
||||||
FlagAdd(MailIdent, Vec<Flag>),
|
FlagAdd(UniqueIdent, Vec<Flag>),
|
||||||
FlagDel(MailIdent, Vec<Flag>),
|
FlagDel(UniqueIdent, Vec<Flag>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UidIndex {
|
impl UidIndex {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn op_mail_add(&self, ident: MailIdent, flags: Vec<Flag>) -> UidIndexOp {
|
pub fn op_mail_add(&self, ident: UniqueIdent, flags: Vec<Flag>) -> UidIndexOp {
|
||||||
UidIndexOp::MailAdd(ident, self.internalseq, flags)
|
UidIndexOp::MailAdd(ident, self.internalseq, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn op_mail_del(&self, ident: MailIdent) -> UidIndexOp {
|
pub fn op_mail_del(&self, ident: UniqueIdent) -> UidIndexOp {
|
||||||
UidIndexOp::MailDel(ident)
|
UidIndexOp::MailDel(ident)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn op_flag_add(&self, ident: MailIdent, flags: Vec<Flag>) -> UidIndexOp {
|
pub fn op_flag_add(&self, ident: UniqueIdent, flags: Vec<Flag>) -> UidIndexOp {
|
||||||
UidIndexOp::FlagAdd(ident, flags)
|
UidIndexOp::FlagAdd(ident, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn op_flag_del(&self, ident: MailIdent, flags: Vec<Flag>) -> UidIndexOp {
|
pub fn op_flag_del(&self, ident: UniqueIdent, flags: Vec<Flag>) -> UidIndexOp {
|
||||||
UidIndexOp::FlagDel(ident, flags)
|
UidIndexOp::FlagDel(ident, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
// INTERNAL functions to keep state consistent
|
// INTERNAL functions to keep state consistent
|
||||||
|
|
||||||
fn reg_email(&mut self, ident: MailIdent, uid: ImapUid, flags: &Vec<Flag>) {
|
fn reg_email(&mut self, ident: UniqueIdent, uid: ImapUid, flags: &Vec<Flag>) {
|
||||||
// Insert the email in our table
|
// Insert the email in our table
|
||||||
self.table.insert(ident, (uid, flags.clone()));
|
self.table.insert(ident, (uid, flags.clone()));
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ impl UidIndex {
|
||||||
self.idx_by_flag.insert(uid, flags);
|
self.idx_by_flag.insert(uid, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unreg_email(&mut self, ident: &MailIdent) {
|
fn unreg_email(&mut self, ident: &UniqueIdent) {
|
||||||
// We do nothing if the mail does not exist
|
// We do nothing if the mail does not exist
|
||||||
let (uid, flags) = match self.table.get(ident) {
|
let (uid, flags) = match self.table.get(ident) {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
|
@ -198,7 +198,7 @@ impl FlagIndex {
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct UidIndexSerializedRepr {
|
struct UidIndexSerializedRepr {
|
||||||
mails: Vec<(ImapUid, MailIdent, Vec<Flag>)>,
|
mails: Vec<(ImapUid, UniqueIdent, Vec<Flag>)>,
|
||||||
uidvalidity: ImapUidvalidity,
|
uidvalidity: ImapUidvalidity,
|
||||||
uidnext: ImapUid,
|
uidnext: ImapUid,
|
||||||
internalseq: ImapUid,
|
internalseq: ImapUid,
|
||||||
|
@ -261,7 +261,7 @@ mod tests {
|
||||||
|
|
||||||
// Add message 1
|
// Add message 1
|
||||||
{
|
{
|
||||||
let m = MailIdent([0x01; 24]);
|
let m = UniqueIdent([0x01; 24]);
|
||||||
let f = vec!["\\Recent".to_string(), "\\Archive".to_string()];
|
let f = vec!["\\Recent".to_string(), "\\Archive".to_string()];
|
||||||
let ev = state.op_mail_add(m, f);
|
let ev = state.op_mail_add(m, f);
|
||||||
state = state.apply(&ev);
|
state = state.apply(&ev);
|
||||||
|
@ -282,7 +282,7 @@ mod tests {
|
||||||
|
|
||||||
// Add message 2
|
// Add message 2
|
||||||
{
|
{
|
||||||
let m = MailIdent([0x02; 24]);
|
let m = UniqueIdent([0x02; 24]);
|
||||||
let f = vec!["\\Seen".to_string(), "\\Archive".to_string()];
|
let f = vec!["\\Seen".to_string(), "\\Archive".to_string()];
|
||||||
let ev = state.op_mail_add(m, f);
|
let ev = state.op_mail_add(m, f);
|
||||||
state = state.apply(&ev);
|
state = state.apply(&ev);
|
||||||
|
@ -293,7 +293,7 @@ mod tests {
|
||||||
|
|
||||||
// Add flags to message 1
|
// Add flags to message 1
|
||||||
{
|
{
|
||||||
let m = MailIdent([0x01; 24]);
|
let m = UniqueIdent([0x01; 24]);
|
||||||
let f = vec!["Important".to_string(), "$cl_1".to_string()];
|
let f = vec!["Important".to_string(), "$cl_1".to_string()];
|
||||||
let ev = state.op_flag_add(m, f);
|
let ev = state.op_flag_add(m, f);
|
||||||
state = state.apply(&ev);
|
state = state.apply(&ev);
|
||||||
|
@ -301,7 +301,7 @@ mod tests {
|
||||||
|
|
||||||
// Delete flags from message 1
|
// Delete flags from message 1
|
||||||
{
|
{
|
||||||
let m = MailIdent([0x01; 24]);
|
let m = UniqueIdent([0x01; 24]);
|
||||||
let f = vec!["\\Recent".to_string()];
|
let f = vec!["\\Recent".to_string()];
|
||||||
let ev = state.op_flag_del(m, f);
|
let ev = state.op_flag_del(m, f);
|
||||||
state = state.apply(&ev);
|
state = state.apply(&ev);
|
||||||
|
@ -312,7 +312,7 @@ mod tests {
|
||||||
|
|
||||||
// Delete message 2
|
// Delete message 2
|
||||||
{
|
{
|
||||||
let m = MailIdent([0x02; 24]);
|
let m = UniqueIdent([0x02; 24]);
|
||||||
let ev = state.op_mail_del(m);
|
let ev = state.op_mail_del(m);
|
||||||
state = state.apply(&ev);
|
state = state.apply(&ev);
|
||||||
|
|
||||||
|
@ -322,7 +322,7 @@ mod tests {
|
||||||
|
|
||||||
// Add a message 3 concurrent to message 1 (trigger a uid validity change)
|
// Add a message 3 concurrent to message 1 (trigger a uid validity change)
|
||||||
{
|
{
|
||||||
let m = MailIdent([0x03; 24]);
|
let m = UniqueIdent([0x03; 24]);
|
||||||
let f = vec!["\\Archive".to_string(), "\\Recent".to_string()];
|
let f = vec!["\\Archive".to_string(), "\\Recent".to_string()];
|
||||||
let ev = UidIndexOp::MailAdd(m, 1, f);
|
let ev = UidIndexOp::MailAdd(m, 1, f);
|
||||||
state = state.apply(&ev);
|
state = state.apply(&ev);
|
||||||
|
@ -334,7 +334,7 @@ mod tests {
|
||||||
assert!(state.uidvalidity > 1);
|
assert!(state.uidvalidity > 1);
|
||||||
|
|
||||||
let (last_uid, ident) = state.idx_by_uid.get_max().unwrap();
|
let (last_uid, ident) = state.idx_by_uid.get_max().unwrap();
|
||||||
assert_eq!(ident, &MailIdent([0x03; 24]));
|
assert_eq!(ident, &UniqueIdent([0x03; 24]));
|
||||||
|
|
||||||
let archive = state.idx_by_flag.0.get("\\Archive").unwrap();
|
let archive = state.idx_by_flag.0.get("\\Archive").unwrap();
|
||||||
assert_eq!(archive.len(), 2);
|
assert_eq!(archive.len(), 2);
|
||||||
|
|
|
@ -17,7 +17,7 @@ use crate::time::now_msec;
|
||||||
/// Their main property is to be unique without having to rely
|
/// Their main property is to be unique without having to rely
|
||||||
/// on synchronization between IMAP processes.
|
/// on synchronization between IMAP processes.
|
||||||
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
|
||||||
pub struct MailIdent(pub [u8; 24]);
|
pub struct UniqueIdent(pub [u8; 24]);
|
||||||
|
|
||||||
struct IdentGenerator {
|
struct IdentGenerator {
|
||||||
pid: u128,
|
pid: u128,
|
||||||
|
@ -34,12 +34,12 @@ impl IdentGenerator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gen(&self) -> MailIdent {
|
fn gen(&self) -> UniqueIdent {
|
||||||
let sn = self.sn.fetch_add(1, Ordering::Relaxed);
|
let sn = self.sn.fetch_add(1, Ordering::Relaxed);
|
||||||
let mut res = [0u8; 24];
|
let mut res = [0u8; 24];
|
||||||
res[0..16].copy_from_slice(&u128::to_be_bytes(self.pid));
|
res[0..16].copy_from_slice(&u128::to_be_bytes(self.pid));
|
||||||
res[16..24].copy_from_slice(&u64::to_be_bytes(sn));
|
res[16..24].copy_from_slice(&u64::to_be_bytes(sn));
|
||||||
MailIdent(res)
|
UniqueIdent(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,23 +47,23 @@ lazy_static! {
|
||||||
static ref GENERATOR: IdentGenerator = IdentGenerator::new();
|
static ref GENERATOR: IdentGenerator = IdentGenerator::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_ident() -> MailIdent {
|
pub fn gen_ident() -> UniqueIdent {
|
||||||
GENERATOR.gen()
|
GENERATOR.gen()
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- serde --
|
// -- serde --
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for MailIdent {
|
impl<'de> Deserialize<'de> for UniqueIdent {
|
||||||
fn deserialize<D>(d: D) -> Result<Self, D::Error>
|
fn deserialize<D>(d: D) -> Result<Self, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let v = String::deserialize(d)?;
|
let v = String::deserialize(d)?;
|
||||||
MailIdent::from_str(&v).map_err(D::Error::custom)
|
UniqueIdent::from_str(&v).map_err(D::Error::custom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for MailIdent {
|
impl Serialize for UniqueIdent {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: Serializer,
|
||||||
|
@ -72,16 +72,16 @@ impl Serialize for MailIdent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for MailIdent {
|
impl std::fmt::Display for UniqueIdent {
|
||||||
fn to_string(&self) -> String {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
hex::encode(self.0)
|
write!(f, "{}", hex::encode(self.0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for MailIdent {
|
impl FromStr for UniqueIdent {
|
||||||
type Err = &'static str;
|
type Err = &'static str;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<MailIdent, &'static str> {
|
fn from_str(s: &str) -> Result<UniqueIdent, &'static str> {
|
||||||
let bytes = hex::decode(s).map_err(|_| "invalid hex")?;
|
let bytes = hex::decode(s).map_err(|_| "invalid hex")?;
|
||||||
|
|
||||||
if bytes.len() != 24 {
|
if bytes.len() != 24 {
|
||||||
|
@ -90,6 +90,6 @@ impl FromStr for MailIdent {
|
||||||
|
|
||||||
let mut tmp = [0u8; 24];
|
let mut tmp = [0u8; 24];
|
||||||
tmp[..].copy_from_slice(&bytes);
|
tmp[..].copy_from_slice(&bytes);
|
||||||
Ok(MailIdent(tmp))
|
Ok(UniqueIdent(tmp))
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -9,6 +9,7 @@ use rusoto_s3::S3Client;
|
||||||
|
|
||||||
use crate::login::{Credentials, StorageCredentials};
|
use crate::login::{Credentials, StorageCredentials};
|
||||||
use crate::mail::mailbox::Mailbox;
|
use crate::mail::mailbox::Mailbox;
|
||||||
|
use crate::mail::unique_ident::UniqueIdent;
|
||||||
|
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
|
@ -36,21 +37,29 @@ impl User {
|
||||||
|
|
||||||
/// Opens an existing mailbox given its IMAP name.
|
/// Opens an existing mailbox given its IMAP name.
|
||||||
pub async fn open_mailbox(&self, name: &str) -> Result<Option<Arc<Mailbox>>> {
|
pub async fn open_mailbox(&self, name: &str) -> Result<Option<Arc<Mailbox>>> {
|
||||||
|
// TODO: handle mailbox names, mappings, renaming, etc
|
||||||
|
let id = match name {
|
||||||
|
"INBOX" => UniqueIdent([0u8; 24]),
|
||||||
|
_ => panic!("Only INBOX exists for now"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let cache_key = (self.creds.storage.clone(), id);
|
||||||
|
|
||||||
{
|
{
|
||||||
let cache = MAILBOX_CACHE.cache.lock().unwrap();
|
let cache = MAILBOX_CACHE.cache.lock().unwrap();
|
||||||
if let Some(mb) = cache.get(&self.creds.storage).and_then(Weak::upgrade) {
|
if let Some(mb) = cache.get(&cache_key).and_then(Weak::upgrade) {
|
||||||
return Ok(Some(mb));
|
return Ok(Some(mb));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mb = Arc::new(Mailbox::open(&self.creds, name).await?);
|
let mb = Arc::new(Mailbox::open(&self.creds, id).await?);
|
||||||
|
|
||||||
let mut cache = MAILBOX_CACHE.cache.lock().unwrap();
|
let mut cache = MAILBOX_CACHE.cache.lock().unwrap();
|
||||||
if let Some(concurrent_mb) = cache.get(&self.creds.storage).and_then(Weak::upgrade) {
|
if let Some(concurrent_mb) = cache.get(&cache_key).and_then(Weak::upgrade) {
|
||||||
drop(mb); // we worked for nothing but at least we didn't starve someone else
|
drop(mb); // we worked for nothing but at least we didn't starve someone else
|
||||||
Ok(Some(concurrent_mb))
|
Ok(Some(concurrent_mb))
|
||||||
} else {
|
} else {
|
||||||
cache.insert(self.creds.storage.clone(), Arc::downgrade(&mb));
|
cache.insert(cache_key, Arc::downgrade(&mb));
|
||||||
Ok(Some(mb))
|
Ok(Some(mb))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +83,7 @@ impl User {
|
||||||
// ---- Mailbox cache ----
|
// ---- Mailbox cache ----
|
||||||
|
|
||||||
struct MailboxCache {
|
struct MailboxCache {
|
||||||
cache: std::sync::Mutex<HashMap<StorageCredentials, Weak<Mailbox>>>,
|
cache: std::sync::Mutex<HashMap<(StorageCredentials, UniqueIdent), Weak<Mailbox>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MailboxCache {
|
impl MailboxCache {
|
||||||
|
|
Loading…
Reference in a new issue