Compare commits
5 Commits
eb8d29813c
...
a4ae4da8b1
Author | SHA1 | Date |
---|---|---|
Manos Pitsidianakis | a4ae4da8b1 | |
Manos Pitsidianakis | 4050f6893f | |
Manos Pitsidianakis | dcccd303ac | |
Manos Pitsidianakis | 22a64e2d76 | |
Manos Pitsidianakis | 781a1d0e1b |
|
@ -418,6 +418,8 @@ Copy or move to other mailbox.
|
|||
Copy or move to another account's mailbox.
|
||||
.It Cm delete
|
||||
Delete selected threads.
|
||||
.It Cm export-mbox Ar FILEPATH
|
||||
Export selected threads to mboxcl2 file.
|
||||
.It Cm create-mailbox Ar ACCOUNT Ar MAILBOX_PATH
|
||||
create mailbox with given path.
|
||||
Be careful with backends and separator sensitivity (eg IMAP)
|
||||
|
|
|
@ -58,15 +58,15 @@ use self::maildir::MaildirType;
|
|||
use self::mbox::MboxType;
|
||||
use super::email::{Envelope, EnvelopeHash, Flag};
|
||||
use std::any::Any;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Deref;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
pub use futures::stream::Stream;
|
||||
use futures::stream::Stream;
|
||||
use std::future::Future;
|
||||
pub use std::pin::Pin;
|
||||
use std::pin::Pin;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
@ -348,9 +348,7 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
|
|||
mailbox_hash: MailboxHash,
|
||||
) -> ResultFuture<()>;
|
||||
|
||||
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
|
||||
None
|
||||
}
|
||||
fn collection(&self) -> crate::Collection;
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
|
||||
|
|
|
@ -42,20 +42,21 @@ use crate::backends::{
|
|||
*,
|
||||
};
|
||||
|
||||
use crate::collection::Collection;
|
||||
use crate::conf::AccountSettings;
|
||||
use crate::connections::timeout;
|
||||
use crate::email::{parser::BytesExt, *};
|
||||
use crate::error::{MeliError, Result, ResultIntoMeliError};
|
||||
use futures::lock::Mutex as FutureMutex;
|
||||
use futures::stream::Stream;
|
||||
use std::collections::{hash_map::DefaultHasher, BTreeMap};
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::collections::{BTreeSet, HashMap, HashSet};
|
||||
use std::convert::TryFrom;
|
||||
use std::convert::TryInto;
|
||||
use std::hash::Hasher;
|
||||
use std::pin::Pin;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
pub type ImapNum = usize;
|
||||
|
@ -142,7 +143,7 @@ pub struct UIDStore {
|
|||
msn_index: Arc<Mutex<HashMap<MailboxHash, Vec<UID>>>>,
|
||||
|
||||
byte_cache: Arc<Mutex<HashMap<UID, EnvelopeCache>>>,
|
||||
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||
collection: Collection,
|
||||
|
||||
/* Offline caching */
|
||||
uidvalidity: Arc<Mutex<HashMap<MailboxHash, UID>>>,
|
||||
|
@ -178,7 +179,7 @@ impl UIDStore {
|
|||
msn_index: Default::default(),
|
||||
byte_cache: Default::default(),
|
||||
mailboxes: Arc::new(FutureMutex::new(Default::default())),
|
||||
tag_index: Arc::new(RwLock::new(Default::default())),
|
||||
collection: Default::default(),
|
||||
is_online: Arc::new(Mutex::new((
|
||||
SystemTime::now(),
|
||||
Err(MeliError::new("Account is uninitialised.")),
|
||||
|
@ -710,7 +711,7 @@ impl MailBackend for ImapType {
|
|||
/* Set flags/tags to true */
|
||||
let mut set_seen = false;
|
||||
let command = {
|
||||
let mut tag_lck = uid_store.tag_index.write().unwrap();
|
||||
let mut tag_lck = uid_store.collection.tag_index.write().unwrap();
|
||||
let mut cmd = format!("UID STORE {}", uids[0]);
|
||||
for uid in uids.iter().skip(1) {
|
||||
cmd = format!("{},{}", cmd, uid);
|
||||
|
@ -859,10 +860,6 @@ impl MailBackend for ImapType {
|
|||
}))
|
||||
}
|
||||
|
||||
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
|
||||
Some(self.uid_store.tag_index.clone())
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
@ -871,6 +868,10 @@ impl MailBackend for ImapType {
|
|||
self
|
||||
}
|
||||
|
||||
fn collection(&self) -> Collection {
|
||||
self.uid_store.collection.clone()
|
||||
}
|
||||
|
||||
fn create_mailbox(
|
||||
&mut self,
|
||||
mut path: String,
|
||||
|
@ -1773,7 +1774,7 @@ async fn fetch_hlpr(state: &mut FetchState) -> Result<Vec<Envelope>> {
|
|||
}
|
||||
env.set_references(value);
|
||||
}
|
||||
let mut tag_lck = uid_store.tag_index.write().unwrap();
|
||||
let mut tag_lck = uid_store.collection.tag_index.write().unwrap();
|
||||
if let Some((flags, keywords)) = flags {
|
||||
env.set_flags(*flags);
|
||||
if !env.is_seen() {
|
||||
|
|
|
@ -239,7 +239,7 @@ mod sqlite3_m {
|
|||
.entry(mailbox_hash)
|
||||
.and_modify(|entry| *entry = uidvalidity)
|
||||
.or_insert(uidvalidity);
|
||||
let mut tag_lck = self.uid_store.tag_index.write().unwrap();
|
||||
let mut tag_lck = self.uid_store.collection.tag_index.write().unwrap();
|
||||
for f in to_str!(&flags).split('\0') {
|
||||
let hash = tag_hash!(f);
|
||||
//debug!("hash {} flag {}", hash, &f);
|
||||
|
|
|
@ -171,7 +171,7 @@ impl ImapConnection {
|
|||
}
|
||||
env.set_references(value);
|
||||
}
|
||||
let mut tag_lck = self.uid_store.tag_index.write().unwrap();
|
||||
let mut tag_lck = self.uid_store.collection.tag_index.write().unwrap();
|
||||
if let Some((flags, keywords)) = flags {
|
||||
env.set_flags(*flags);
|
||||
if !env.is_seen() {
|
||||
|
@ -469,7 +469,7 @@ impl ImapConnection {
|
|||
}
|
||||
env.set_references(value);
|
||||
}
|
||||
let mut tag_lck = self.uid_store.tag_index.write().unwrap();
|
||||
let mut tag_lck = self.uid_store.collection.tag_index.write().unwrap();
|
||||
if let Some((flags, keywords)) = flags {
|
||||
env.set_flags(*flags);
|
||||
if !env.is_seen() {
|
||||
|
|
|
@ -238,7 +238,7 @@ impl ImapConnection {
|
|||
}
|
||||
env.set_references(value);
|
||||
}
|
||||
let mut tag_lck = self.uid_store.tag_index.write().unwrap();
|
||||
let mut tag_lck = self.uid_store.collection.tag_index.write().unwrap();
|
||||
if let Some((flags, keywords)) = flags {
|
||||
env.set_flags(*flags);
|
||||
if !env.is_seen() {
|
||||
|
@ -381,7 +381,7 @@ impl ImapConnection {
|
|||
}
|
||||
env.set_references(value);
|
||||
}
|
||||
let mut tag_lck = self.uid_store.tag_index.write().unwrap();
|
||||
let mut tag_lck = self.uid_store.collection.tag_index.write().unwrap();
|
||||
if let Some((flags, keywords)) = flags {
|
||||
env.set_flags(*flags);
|
||||
if !env.is_seen() {
|
||||
|
|
|
@ -388,7 +388,7 @@ pub async fn examine_updates(
|
|||
}
|
||||
env.set_references(value);
|
||||
}
|
||||
let mut tag_lck = uid_store.tag_index.write().unwrap();
|
||||
let mut tag_lck = uid_store.collection.tag_index.write().unwrap();
|
||||
if let Some((flags, keywords)) = flags {
|
||||
env.set_flags(*flags);
|
||||
if !env.is_seen() {
|
||||
|
|
|
@ -23,12 +23,13 @@ use crate::backends::*;
|
|||
use crate::conf::AccountSettings;
|
||||
use crate::email::*;
|
||||
use crate::error::{MeliError, Result};
|
||||
use crate::Collection;
|
||||
use futures::lock::Mutex as FutureMutex;
|
||||
use isahc::config::RedirectPolicy;
|
||||
use isahc::prelude::HttpClient;
|
||||
use isahc::ResponseExt;
|
||||
use serde_json::Value;
|
||||
use std::collections::{hash_map::DefaultHasher, BTreeMap, HashMap, HashSet};
|
||||
use std::collections::{hash_map::DefaultHasher, HashMap, HashSet};
|
||||
use std::convert::TryFrom;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::str::FromStr;
|
||||
|
@ -183,7 +184,7 @@ pub struct Store {
|
|||
pub id_store: Arc<Mutex<HashMap<EnvelopeHash, Id<EmailObject>>>>,
|
||||
pub reverse_id_store: Arc<Mutex<HashMap<Id<EmailObject>, EnvelopeHash>>>,
|
||||
pub blob_id_store: Arc<Mutex<HashMap<EnvelopeHash, Id<BlobObject>>>>,
|
||||
pub tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||
pub collection: Collection,
|
||||
pub mailboxes: Arc<RwLock<HashMap<MailboxHash, JmapMailbox>>>,
|
||||
pub mailboxes_index: Arc<RwLock<HashMap<MailboxHash, HashSet<EnvelopeHash>>>>,
|
||||
pub mailbox_state: Arc<Mutex<State<MailboxObject>>>,
|
||||
|
@ -194,7 +195,7 @@ pub struct Store {
|
|||
|
||||
impl Store {
|
||||
pub fn add_envelope(&self, obj: EmailObject) -> Envelope {
|
||||
let mut tag_lck = self.tag_index.write().unwrap();
|
||||
let mut tag_lck = self.collection.tag_index.write().unwrap();
|
||||
let tags = obj
|
||||
.keywords()
|
||||
.keys()
|
||||
|
@ -483,10 +484,6 @@ impl MailBackend for JmapType {
|
|||
}))
|
||||
}
|
||||
|
||||
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
|
||||
Some(self.store.tag_index.clone())
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
@ -495,6 +492,10 @@ impl MailBackend for JmapType {
|
|||
self
|
||||
}
|
||||
|
||||
fn collection(&self) -> Collection {
|
||||
self.store.collection.clone()
|
||||
}
|
||||
|
||||
fn search(
|
||||
&self,
|
||||
q: crate::search::Query,
|
||||
|
@ -753,7 +754,7 @@ impl MailBackend for JmapType {
|
|||
}
|
||||
|
||||
{
|
||||
let mut tag_index_lck = store.tag_index.write().unwrap();
|
||||
let mut tag_index_lck = store.collection.tag_index.write().unwrap();
|
||||
for (flag, value) in flags.iter() {
|
||||
match flag {
|
||||
Ok(_) => {}
|
||||
|
@ -841,12 +842,12 @@ impl JmapType {
|
|||
online_status,
|
||||
event_consumer,
|
||||
is_subscribed: Arc::new(IsSubscribedFn(is_subscribed)),
|
||||
collection: Collection::default(),
|
||||
|
||||
byte_cache: Default::default(),
|
||||
id_store: Default::default(),
|
||||
reverse_id_store: Default::default(),
|
||||
blob_id_store: Default::default(),
|
||||
tag_index: Default::default(),
|
||||
mailboxes: Default::default(),
|
||||
mailboxes_index: Default::default(),
|
||||
mailbox_state: Default::default(),
|
||||
|
|
|
@ -30,7 +30,7 @@ use crate::backends::*;
|
|||
use crate::email::Flag;
|
||||
use crate::error::{MeliError, Result};
|
||||
use crate::shellexpand::ShellExpandTrait;
|
||||
pub use futures::stream::Stream;
|
||||
use futures::stream::Stream;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::fs;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
|
|
@ -25,6 +25,7 @@ use crate::conf::AccountSettings;
|
|||
use crate::email::{Envelope, EnvelopeHash, Flag};
|
||||
use crate::error::{ErrorKind, MeliError, Result};
|
||||
use crate::shellexpand::ShellExpandTrait;
|
||||
use crate::Collection;
|
||||
use futures::prelude::Stream;
|
||||
|
||||
extern crate notify;
|
||||
|
@ -109,6 +110,7 @@ pub struct MaildirType {
|
|||
mailbox_index: Arc<Mutex<HashMap<EnvelopeHash, MailboxHash>>>,
|
||||
hash_indexes: HashIndexes,
|
||||
event_consumer: BackendEventConsumer,
|
||||
collection: Collection,
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
|
@ -1003,6 +1005,10 @@ impl MailBackend for MaildirType {
|
|||
}))
|
||||
}
|
||||
|
||||
fn collection(&self) -> Collection {
|
||||
self.collection.clone()
|
||||
}
|
||||
|
||||
fn create_mailbox(
|
||||
&mut self,
|
||||
new_path: String,
|
||||
|
@ -1236,6 +1242,7 @@ impl MaildirType {
|
|||
hash_indexes: Arc::new(Mutex::new(hash_indexes)),
|
||||
mailbox_index: Default::default(),
|
||||
event_consumer,
|
||||
collection: Default::default(),
|
||||
path: root_path,
|
||||
}))
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
*/
|
||||
|
||||
use crate::backends::*;
|
||||
use crate::collection::Collection;
|
||||
use crate::conf::AccountSettings;
|
||||
use crate::email::parser::BytesExt;
|
||||
use crate::email::*;
|
||||
|
@ -47,6 +48,8 @@ use std::str::FromStr;
|
|||
use std::sync::mpsc::channel;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
pub mod write;
|
||||
|
||||
type Offset = usize;
|
||||
type Length = usize;
|
||||
|
||||
|
@ -271,14 +274,14 @@ impl BackendOp for MboxOp {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum MboxReader {
|
||||
pub enum MboxFormat {
|
||||
MboxO,
|
||||
MboxRd,
|
||||
MboxCl,
|
||||
MboxCl2,
|
||||
}
|
||||
|
||||
impl Default for MboxReader {
|
||||
impl Default for MboxFormat {
|
||||
fn default() -> Self {
|
||||
Self::MboxCl2
|
||||
}
|
||||
|
@ -321,8 +324,8 @@ macro_rules! find_From__line {
|
|||
}};
|
||||
}
|
||||
|
||||
impl MboxReader {
|
||||
fn parse<'i>(&self, input: &'i [u8]) -> IResult<&'i [u8], Envelope> {
|
||||
impl MboxFormat {
|
||||
pub fn parse<'i>(&self, input: &'i [u8]) -> IResult<&'i [u8], Envelope> {
|
||||
let orig_input = input;
|
||||
let mut input = input;
|
||||
match self {
|
||||
|
@ -605,7 +608,7 @@ pub fn mbox_parse(
|
|||
index: Arc<Mutex<HashMap<EnvelopeHash, (Offset, Length)>>>,
|
||||
input: &[u8],
|
||||
file_offset: usize,
|
||||
reader: Option<MboxReader>,
|
||||
format: Option<MboxFormat>,
|
||||
) -> IResult<&[u8], Vec<Envelope>> {
|
||||
if input.is_empty() {
|
||||
return Err(nom::Err::Error((input, ErrorKind::Tag)));
|
||||
|
@ -614,9 +617,9 @@ pub fn mbox_parse(
|
|||
let mut index = index.lock().unwrap();
|
||||
let mut envelopes = Vec::with_capacity(32);
|
||||
|
||||
let reader = reader.unwrap_or(MboxReader::MboxCl2);
|
||||
let format = format.unwrap_or(MboxFormat::MboxCl2);
|
||||
while !input[offset + file_offset..].is_empty() {
|
||||
let (next_input, env) = match reader.parse(&input[offset + file_offset..]) {
|
||||
let (next_input, env) = match format.parse(&input[offset + file_offset..]) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
// Try to recover from this error by finding a new candidate From_ line
|
||||
|
@ -648,12 +651,12 @@ pub fn mbox_parse(
|
|||
Ok((&[], envelopes))
|
||||
}
|
||||
|
||||
struct MessageIterator<'a> {
|
||||
index: Arc<Mutex<HashMap<EnvelopeHash, (Offset, Length)>>>,
|
||||
input: &'a [u8],
|
||||
file_offset: usize,
|
||||
offset: usize,
|
||||
reader: Option<MboxReader>,
|
||||
pub struct MessageIterator<'a> {
|
||||
pub index: Arc<Mutex<HashMap<EnvelopeHash, (Offset, Length)>>>,
|
||||
pub input: &'a [u8],
|
||||
pub file_offset: usize,
|
||||
pub offset: usize,
|
||||
pub format: Option<MboxFormat>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for MessageIterator<'a> {
|
||||
|
@ -664,10 +667,10 @@ impl<'a> Iterator for MessageIterator<'a> {
|
|||
}
|
||||
let mut index = self.index.lock().unwrap();
|
||||
|
||||
let reader = self.reader.unwrap_or(MboxReader::MboxCl2);
|
||||
let format = self.format.unwrap_or(MboxFormat::MboxCl2);
|
||||
while !self.input[self.offset + self.file_offset..].is_empty() {
|
||||
let (next_input, env) =
|
||||
match reader.parse(&self.input[self.offset + self.file_offset..]) {
|
||||
match format.parse(&self.input[self.offset + self.file_offset..]) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
// Try to recover from this error by finding a new candidate From_ line
|
||||
|
@ -708,9 +711,10 @@ impl<'a> Iterator for MessageIterator<'a> {
|
|||
pub struct MboxType {
|
||||
account_name: String,
|
||||
path: PathBuf,
|
||||
collection: Collection,
|
||||
mailbox_index: Arc<Mutex<HashMap<EnvelopeHash, MailboxHash>>>,
|
||||
mailboxes: Arc<Mutex<HashMap<MailboxHash, MboxMailbox>>>,
|
||||
prefer_mbox_type: Option<MboxReader>,
|
||||
prefer_mbox_type: Option<MboxFormat>,
|
||||
event_consumer: BackendEventConsumer,
|
||||
}
|
||||
|
||||
|
@ -739,7 +743,7 @@ impl MailBackend for MboxType {
|
|||
mailbox_hash: MailboxHash,
|
||||
mailbox_index: Arc<Mutex<HashMap<EnvelopeHash, MailboxHash>>>,
|
||||
mailboxes: Arc<Mutex<HashMap<MailboxHash, MboxMailbox>>>,
|
||||
prefer_mbox_type: Option<MboxReader>,
|
||||
prefer_mbox_type: Option<MboxFormat>,
|
||||
offset: usize,
|
||||
file_offset: usize,
|
||||
contents: Vec<u8>,
|
||||
|
@ -754,7 +758,7 @@ impl MailBackend for MboxType {
|
|||
input: &self.contents.as_slice(),
|
||||
offset: self.offset,
|
||||
file_offset: self.file_offset,
|
||||
reader: self.prefer_mbox_type,
|
||||
format: self.prefer_mbox_type,
|
||||
};
|
||||
let mut payload = vec![];
|
||||
let mut done = false;
|
||||
|
@ -1057,6 +1061,10 @@ impl MailBackend for MboxType {
|
|||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn collection(&self) -> Collection {
|
||||
self.collection.clone()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! get_conf_val {
|
||||
|
@ -1108,10 +1116,10 @@ impl MboxType {
|
|||
path,
|
||||
prefer_mbox_type: match prefer_mbox_type.as_str() {
|
||||
"auto" => None,
|
||||
"mboxo" => Some(MboxReader::MboxO),
|
||||
"mboxrd" => Some(MboxReader::MboxRd),
|
||||
"mboxcl" => Some(MboxReader::MboxCl),
|
||||
"mboxcl2" => Some(MboxReader::MboxCl2),
|
||||
"mboxo" => Some(MboxFormat::MboxO),
|
||||
"mboxrd" => Some(MboxFormat::MboxRd),
|
||||
"mboxcl" => Some(MboxFormat::MboxCl),
|
||||
"mboxcl2" => Some(MboxFormat::MboxCl2),
|
||||
_ => {
|
||||
return Err(MeliError::new(format!(
|
||||
"{} invalid `prefer_mbox_type` value: `{}`",
|
||||
|
@ -1120,6 +1128,7 @@ impl MboxType {
|
|||
)))
|
||||
}
|
||||
},
|
||||
collection: Collection::default(),
|
||||
mailbox_index: Default::default(),
|
||||
mailboxes: Default::default(),
|
||||
};
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* meli - mailbox module.
|
||||
*
|
||||
* Copyright 2021 Manos Pitsidianakis
|
||||
*
|
||||
* This file is part of meli.
|
||||
*
|
||||
* meli is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* meli is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
|
||||
impl MboxFormat {
|
||||
pub fn append(
|
||||
&self,
|
||||
writer: &mut dyn std::io::Write,
|
||||
input: &[u8],
|
||||
envelope_from: Option<&Address>,
|
||||
delivery_date: Option<crate::UnixTimestamp>,
|
||||
is_empty: bool,
|
||||
crlf: bool,
|
||||
) -> Result<()> {
|
||||
let line_ending: &'static [u8] = if crlf { &b"\r\n"[..] } else { &b"\n"[..] };
|
||||
if !is_empty {
|
||||
writer.write_all(line_ending)?;
|
||||
writer.write_all(line_ending)?;
|
||||
}
|
||||
writer.write_all(&b"From "[..])?;
|
||||
if let Some(from) = envelope_from {
|
||||
writer.write_all(from.address_spec_raw())?;
|
||||
} else {
|
||||
writer.write_all(&b"MAILER-DAEMON"[..])?;
|
||||
}
|
||||
writer.write_all(&b" "[..])?;
|
||||
writer.write_all(
|
||||
crate::datetime::timestamp_to_string(
|
||||
delivery_date.unwrap_or_else(|| crate::datetime::now()),
|
||||
Some(crate::datetime::ASCTIME_FMT),
|
||||
true,
|
||||
)
|
||||
.trim()
|
||||
.as_bytes(),
|
||||
)?;
|
||||
writer.write_all(line_ending)?;
|
||||
let (mut headers, body) = parser::mail(input)?;
|
||||
match self {
|
||||
MboxFormat::MboxO | MboxFormat::MboxRd => Err(MeliError::new("Unimplemented.")),
|
||||
MboxFormat::MboxCl => {
|
||||
headers.retain(|(header_name, _)| {
|
||||
!header_name.eq_ignore_ascii_case(b"Content-Length")
|
||||
});
|
||||
let len = (body.len()
|
||||
+ body
|
||||
.windows(b"\nFrom ".len())
|
||||
.filter(|w| w == b"\nFrom ")
|
||||
.count()
|
||||
+ if body.starts_with(b"From ") { 1 } else { 0 })
|
||||
.to_string();
|
||||
for (h, v) in headers
|
||||
.into_iter()
|
||||
.chain(Some((&b"Content-Length"[..], len.as_bytes())))
|
||||
{
|
||||
writer.write_all(h)?;
|
||||
writer.write_all(&b": "[..])?;
|
||||
writer.write_all(v)?;
|
||||
writer.write_all(line_ending)?;
|
||||
}
|
||||
writer.write_all(line_ending)?;
|
||||
|
||||
if body.starts_with(b"From ") {
|
||||
writer.write_all(&[b'>'])?;
|
||||
}
|
||||
for i in 0..body.len() {
|
||||
writer.write_all(&[body[i]])?;
|
||||
if body[i..].starts_with(b"\nFrom ") {
|
||||
writer.write_all(&[b'>'])?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
MboxFormat::MboxCl2 => {
|
||||
headers.retain(|(header_name, _)| {
|
||||
!header_name.eq_ignore_ascii_case(b"Content-Length")
|
||||
});
|
||||
let len = body.len().to_string();
|
||||
for (h, v) in headers
|
||||
.into_iter()
|
||||
.chain(Some((&b"Content-Length"[..], len.as_bytes())))
|
||||
{
|
||||
writer.write_all(h)?;
|
||||
writer.write_all(&b": "[..])?;
|
||||
writer.write_all(v)?;
|
||||
writer.write_all(line_ending)?;
|
||||
}
|
||||
writer.write_all(line_ending)?;
|
||||
writer.write_all(body)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,18 +32,18 @@ pub use operations::*;
|
|||
mod connection;
|
||||
pub use connection::*;
|
||||
|
||||
use crate::backends::*;
|
||||
use crate::conf::AccountSettings;
|
||||
use crate::email::*;
|
||||
use crate::error::{MeliError, Result, ResultIntoMeliError};
|
||||
use crate::{backends::*, Collection};
|
||||
use futures::lock::Mutex as FutureMutex;
|
||||
use futures::stream::Stream;
|
||||
use std::collections::{hash_map::DefaultHasher, BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
use std::collections::{hash_map::DefaultHasher, BTreeSet, HashMap, HashSet};
|
||||
use std::future::Future;
|
||||
use std::hash::Hasher;
|
||||
use std::pin::Pin;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Instant;
|
||||
pub type UID = usize;
|
||||
|
||||
|
@ -77,6 +77,7 @@ pub struct UIDStore {
|
|||
hash_index: Arc<Mutex<HashMap<EnvelopeHash, (UID, MailboxHash)>>>,
|
||||
uid_index: Arc<Mutex<HashMap<(MailboxHash, UID), EnvelopeHash>>>,
|
||||
|
||||
collection: Collection,
|
||||
mailboxes: Arc<FutureMutex<HashMap<MailboxHash, NntpMailbox>>>,
|
||||
is_online: Arc<Mutex<(Instant, Result<()>)>>,
|
||||
event_consumer: BackendEventConsumer,
|
||||
|
@ -97,6 +98,7 @@ impl UIDStore {
|
|||
hash_index: Default::default(),
|
||||
uid_index: Default::default(),
|
||||
mailboxes: Arc::new(FutureMutex::new(Default::default())),
|
||||
collection: Collection::new(),
|
||||
is_online: Arc::new(Mutex::new((
|
||||
Instant::now(),
|
||||
Err(MeliError::new("Account is uninitialised.")),
|
||||
|
@ -294,10 +296,6 @@ impl MailBackend for NntpType {
|
|||
Err(MeliError::new("NNTP doesn't support deletion."))
|
||||
}
|
||||
|
||||
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
@ -306,6 +304,10 @@ impl MailBackend for NntpType {
|
|||
self
|
||||
}
|
||||
|
||||
fn collection(&self) -> Collection {
|
||||
self.uid_store.collection.clone()
|
||||
}
|
||||
|
||||
fn create_mailbox(
|
||||
&mut self,
|
||||
_path: String,
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use crate::backends::*;
|
||||
use crate::conf::AccountSettings;
|
||||
use crate::email::{Envelope, EnvelopeHash, Flag};
|
||||
use crate::error::{MeliError, Result};
|
||||
use crate::shellexpand::ShellExpandTrait;
|
||||
use crate::{backends::*, Collection};
|
||||
use smallvec::SmallVec;
|
||||
use std::collections::{
|
||||
hash_map::{DefaultHasher, HashMap},
|
||||
|
@ -220,7 +220,7 @@ pub struct NotmuchDb {
|
|||
mailboxes: Arc<RwLock<HashMap<MailboxHash, NotmuchMailbox>>>,
|
||||
index: Arc<RwLock<HashMap<EnvelopeHash, CString>>>,
|
||||
mailbox_index: Arc<RwLock<HashMap<EnvelopeHash, SmallVec<[MailboxHash; 16]>>>>,
|
||||
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||
collection: Collection,
|
||||
path: PathBuf,
|
||||
account_name: Arc<String>,
|
||||
account_hash: AccountHash,
|
||||
|
@ -358,7 +358,7 @@ impl NotmuchDb {
|
|||
path,
|
||||
index: Arc::new(RwLock::new(Default::default())),
|
||||
mailbox_index: Arc::new(RwLock::new(Default::default())),
|
||||
tag_index: Arc::new(RwLock::new(Default::default())),
|
||||
collection: Collection::default(),
|
||||
|
||||
mailboxes: Arc::new(RwLock::new(mailboxes)),
|
||||
save_messages_to: None,
|
||||
|
@ -510,7 +510,7 @@ impl MailBackend for NotmuchDb {
|
|||
)?);
|
||||
let index = self.index.clone();
|
||||
let mailbox_index = self.mailbox_index.clone();
|
||||
let tag_index = self.tag_index.clone();
|
||||
let tag_index = self.collection.tag_index.clone();
|
||||
let mailboxes = self.mailboxes.clone();
|
||||
let v: Vec<CString>;
|
||||
{
|
||||
|
@ -561,7 +561,7 @@ impl MailBackend for NotmuchDb {
|
|||
let mailboxes = self.mailboxes.clone();
|
||||
let index = self.index.clone();
|
||||
let mailbox_index = self.mailbox_index.clone();
|
||||
let tag_index = self.tag_index.clone();
|
||||
let tag_index = self.collection.tag_index.clone();
|
||||
let event_consumer = self.event_consumer.clone();
|
||||
Ok(Box::pin(async move {
|
||||
let new_revision_uuid = database.get_revision_uuid();
|
||||
|
@ -586,13 +586,13 @@ impl MailBackend for NotmuchDb {
|
|||
use notify::{watcher, RecursiveMode, Watcher};
|
||||
|
||||
let account_hash = self.account_hash;
|
||||
let collection = self.collection.clone();
|
||||
let lib = self.lib.clone();
|
||||
let path = self.path.clone();
|
||||
let revision_uuid = self.revision_uuid.clone();
|
||||
let mailboxes = self.mailboxes.clone();
|
||||
let index = self.index.clone();
|
||||
let mailbox_index = self.mailbox_index.clone();
|
||||
let tag_index = self.tag_index.clone();
|
||||
let event_consumer = self.event_consumer.clone();
|
||||
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
@ -616,7 +616,7 @@ impl MailBackend for NotmuchDb {
|
|||
mailboxes.clone(),
|
||||
index.clone(),
|
||||
mailbox_index.clone(),
|
||||
tag_index.clone(),
|
||||
collection.tag_index.clone(),
|
||||
account_hash.clone(),
|
||||
event_consumer.clone(),
|
||||
new_revision_uuid,
|
||||
|
@ -651,7 +651,7 @@ impl MailBackend for NotmuchDb {
|
|||
hash,
|
||||
index: self.index.clone(),
|
||||
bytes: None,
|
||||
tag_index: self.tag_index.clone(),
|
||||
collection: self.collection.clone(),
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -693,7 +693,7 @@ impl MailBackend for NotmuchDb {
|
|||
self.lib.clone(),
|
||||
true,
|
||||
)?;
|
||||
let tag_index = self.tag_index.clone();
|
||||
let collection = self.collection.clone();
|
||||
let index = self.index.clone();
|
||||
|
||||
Ok(Box::pin(async move {
|
||||
|
@ -781,7 +781,11 @@ impl MailBackend for NotmuchDb {
|
|||
for (f, v) in flags.iter() {
|
||||
if let (Err(tag), true) = (f, v) {
|
||||
let hash = tag_hash!(tag);
|
||||
tag_index.write().unwrap().insert(hash, tag.to_string());
|
||||
collection
|
||||
.tag_index
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert(hash, tag.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -834,8 +838,8 @@ impl MailBackend for NotmuchDb {
|
|||
}))
|
||||
}
|
||||
|
||||
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
|
||||
Some(self.tag_index.clone())
|
||||
fn collection(&self) -> Collection {
|
||||
self.collection.clone()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
|
@ -851,7 +855,7 @@ impl MailBackend for NotmuchDb {
|
|||
struct NotmuchOp {
|
||||
hash: EnvelopeHash,
|
||||
index: Arc<RwLock<HashMap<EnvelopeHash, CString>>>,
|
||||
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||
collection: Collection,
|
||||
database: Arc<DbConnection>,
|
||||
bytes: Option<Vec<u8>>,
|
||||
lib: Arc<libloading::Library>,
|
||||
|
|
|
@ -25,7 +25,7 @@ use smallvec::SmallVec;
|
|||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
|
||||
pub struct EnvelopeRef<'g> {
|
||||
guard: RwLockReadGuard<'g, HashMap<EnvelopeHash, Envelope>>,
|
||||
|
@ -64,8 +64,9 @@ pub struct Collection {
|
|||
pub envelopes: Arc<RwLock<HashMap<EnvelopeHash, Envelope>>>,
|
||||
pub message_id_index: Arc<RwLock<HashMap<Vec<u8>, EnvelopeHash>>>,
|
||||
pub threads: Arc<RwLock<HashMap<MailboxHash, Threads>>>,
|
||||
sent_mailbox: Arc<RwLock<Option<MailboxHash>>>,
|
||||
pub sent_mailbox: Arc<RwLock<Option<MailboxHash>>>,
|
||||
pub mailboxes: Arc<RwLock<HashMap<MailboxHash, HashSet<EnvelopeHash>>>>,
|
||||
pub tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||
}
|
||||
|
||||
impl Default for Collection {
|
||||
|
@ -115,6 +116,7 @@ impl Collection {
|
|||
|
||||
Collection {
|
||||
envelopes: Arc::new(RwLock::new(Default::default())),
|
||||
tag_index: Arc::new(RwLock::new(BTreeMap::default())),
|
||||
message_id_index,
|
||||
threads,
|
||||
mailboxes,
|
||||
|
|
|
@ -48,6 +48,8 @@ pub const RFC3339_FMT: &str = "%Y-%m-%d\0";
|
|||
pub const RFC822_FMT_WITH_TIME: &str = "%a, %e %h %Y %H:%M:%S \0";
|
||||
pub const RFC822_FMT: &str = "%e %h %Y %H:%M:%S \0";
|
||||
pub const DEFAULT_FMT: &str = "%a, %d %b %Y %R\0";
|
||||
//"Tue May 21 13:46:22 1991\n"
|
||||
pub const ASCTIME_FMT: &str = "%a %b %d %H:%M:%S %Y\n\0";
|
||||
|
||||
extern "C" {
|
||||
fn strptime(
|
||||
|
|
|
@ -44,7 +44,6 @@ pub use iterators::*;
|
|||
use crate::text_processing::grapheme_clusters::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt;
|
||||
|
@ -131,7 +130,7 @@ macro_rules! make {
|
|||
e.parent = Some($p);
|
||||
});
|
||||
let old_group = std::mem::replace($threads.groups.entry(old_group_hash).or_default(), ThreadGroup::Node {
|
||||
parent: RefCell::new(parent_group_hash),
|
||||
parent: Arc::new(RwLock::new(parent_group_hash)),
|
||||
});
|
||||
$threads.thread_nodes.entry($c).and_modify(|e| {
|
||||
e.group = parent_group_hash;
|
||||
|
@ -292,7 +291,7 @@ pub struct Thread {
|
|||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub enum ThreadGroup {
|
||||
Root(Thread),
|
||||
Node { parent: RefCell<ThreadHash> },
|
||||
Node { parent: Arc<RwLock<ThreadHash>> },
|
||||
}
|
||||
|
||||
impl Default for ThreadGroup {
|
||||
|
@ -411,16 +410,16 @@ impl ThreadNode {
|
|||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct Threads {
|
||||
pub thread_nodes: HashMap<ThreadNodeHash, ThreadNode>,
|
||||
root_set: RefCell<Vec<ThreadNodeHash>>,
|
||||
tree_index: RefCell<Vec<ThreadNodeHash>>,
|
||||
root_set: Arc<RwLock<Vec<ThreadNodeHash>>>,
|
||||
tree_index: Arc<RwLock<Vec<ThreadNodeHash>>>,
|
||||
pub groups: HashMap<ThreadHash, ThreadGroup>,
|
||||
|
||||
message_ids: HashMap<Vec<u8>, ThreadNodeHash>,
|
||||
pub message_ids_set: HashSet<Vec<u8>>,
|
||||
pub missing_message_ids: HashSet<Vec<u8>>,
|
||||
pub hash_set: HashSet<EnvelopeHash>,
|
||||
sort: RefCell<(SortField, SortOrder)>,
|
||||
subsort: RefCell<(SortField, SortOrder)>,
|
||||
sort: Arc<RwLock<(SortField, SortOrder)>>,
|
||||
subsort: Arc<RwLock<(SortField, SortOrder)>>,
|
||||
}
|
||||
|
||||
impl PartialEq for ThreadNode {
|
||||
|
@ -454,13 +453,13 @@ impl Threads {
|
|||
pub fn find_group(&self, h: ThreadHash) -> ThreadHash {
|
||||
let p = match self.groups[&h] {
|
||||
ThreadGroup::Root(_) => return h,
|
||||
ThreadGroup::Node { ref parent } => *parent.borrow(),
|
||||
ThreadGroup::Node { ref parent } => *parent.read().unwrap(),
|
||||
};
|
||||
|
||||
let parent_group = self.find_group(p);
|
||||
match self.groups[&h] {
|
||||
ThreadGroup::Node { ref parent } => {
|
||||
*parent.borrow_mut() = parent_group;
|
||||
*parent.write().unwrap() = parent_group;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -491,8 +490,8 @@ impl Threads {
|
|||
message_ids_set,
|
||||
missing_message_ids,
|
||||
hash_set,
|
||||
sort: RefCell::new((SortField::Date, SortOrder::Desc)),
|
||||
subsort: RefCell::new((SortField::Subject, SortOrder::Desc)),
|
||||
sort: Arc::new(RwLock::new((SortField::Date, SortOrder::Desc))),
|
||||
subsort: Arc::new(RwLock::new((SortField::Subject, SortOrder::Desc))),
|
||||
|
||||
..Default::default()
|
||||
}
|
||||
|
@ -573,7 +572,7 @@ impl Threads {
|
|||
};
|
||||
|
||||
if self.thread_nodes[&t_id].parent.is_none() {
|
||||
let mut tree_index = self.tree_index.borrow_mut();
|
||||
let mut tree_index = self.tree_index.write().unwrap();
|
||||
if let Some(i) = tree_index.iter().position(|t| *t == t_id) {
|
||||
tree_index.remove(i);
|
||||
}
|
||||
|
@ -845,7 +844,7 @@ impl Threads {
|
|||
|
||||
/*
|
||||
save_graph(
|
||||
&self.tree_index.borrow(),
|
||||
&self.tree_index.read().unwrap(),
|
||||
&self.thread_nodes,
|
||||
&self
|
||||
.message_ids
|
||||
|
@ -871,7 +870,7 @@ impl Threads {
|
|||
ref thread_nodes,
|
||||
..
|
||||
} = self;
|
||||
let tree = &mut tree_index.borrow_mut();
|
||||
let tree = &mut tree_index.write().unwrap();
|
||||
for t in tree.iter_mut() {
|
||||
thread_nodes[t].children.sort_by(|a, b| match subsort {
|
||||
(SortField::Date, SortOrder::Desc) => {
|
||||
|
@ -1090,7 +1089,7 @@ impl Threads {
|
|||
});
|
||||
}
|
||||
fn inner_sort_by(&self, sort: (SortField, SortOrder), envelopes: &Envelopes) {
|
||||
let tree = &mut self.tree_index.borrow_mut();
|
||||
let tree = &mut self.tree_index.write().unwrap();
|
||||
let envelopes = envelopes.read().unwrap();
|
||||
tree.sort_by(|a, b| match sort {
|
||||
(SortField::Date, SortOrder::Desc) => {
|
||||
|
@ -1172,13 +1171,13 @@ impl Threads {
|
|||
subsort: (SortField, SortOrder),
|
||||
envelopes: &Envelopes,
|
||||
) {
|
||||
if *self.sort.borrow() != sort {
|
||||
if *self.sort.read().unwrap() != sort {
|
||||
self.inner_sort_by(sort, envelopes);
|
||||
*self.sort.borrow_mut() = sort;
|
||||
*self.sort.write().unwrap() = sort;
|
||||
}
|
||||
if *self.subsort.borrow() != subsort {
|
||||
if *self.subsort.read().unwrap() != subsort {
|
||||
self.inner_subsort_by(subsort, envelopes);
|
||||
*self.subsort.borrow_mut() = subsort;
|
||||
*self.subsort.write().unwrap() = subsort;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1196,11 +1195,11 @@ impl Threads {
|
|||
}
|
||||
|
||||
pub fn root_len(&self) -> usize {
|
||||
self.tree_index.borrow().len()
|
||||
self.tree_index.read().unwrap().len()
|
||||
}
|
||||
|
||||
pub fn root_set(&self, idx: usize) -> ThreadNodeHash {
|
||||
self.tree_index.borrow()[idx]
|
||||
self.tree_index.read().unwrap()[idx]
|
||||
}
|
||||
|
||||
pub fn roots(&self) -> SmallVec<[ThreadHash; 1024]> {
|
||||
|
|
|
@ -419,6 +419,19 @@ define_commands!([
|
|||
}
|
||||
)
|
||||
},
|
||||
{ tags: ["export-mbox "],
|
||||
desc: "export-mbox PATH",
|
||||
tokens: &[One(Literal("export-mbox")), One(Filepath)],
|
||||
parser:(
|
||||
fn export_mbox(input: &[u8]) -> IResult<&[u8], Action> {
|
||||
let (input, _) = tag("export-mbox")(input.trim())?;
|
||||
let (input, _) = is_a(" ")(input)?;
|
||||
let (input, path) = quoted_argument(input.trim())?;
|
||||
let (input, _) = eof(input)?;
|
||||
Ok((input, Listing(ExportMbox(Some(melib::backends::mbox::MboxFormat::MboxCl2), path.to_string().into()))))
|
||||
}
|
||||
)
|
||||
},
|
||||
{ tags: ["list-archive", "list-post", "list-unsubscribe", "list-"],
|
||||
desc: "list-[unsubscribe/post/archive]",
|
||||
tokens: &[One(Alternatives(&[to_stream!(One(Literal("list-archive"))), to_stream!(One(Literal("list-post"))), to_stream!(One(Literal("list-unsubscribe")))]))],
|
||||
|
@ -852,6 +865,7 @@ fn listing_action(input: &[u8]) -> IResult<&[u8], Action> {
|
|||
select,
|
||||
toggle_thread_snooze,
|
||||
open_in_new_tab,
|
||||
export_mbox,
|
||||
_tag,
|
||||
))(input)
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ pub enum ListingAction {
|
|||
MoveTo(MailboxPath),
|
||||
MoveToOtherAccount(AccountName, MailboxPath),
|
||||
Import(PathBuf, MailboxPath),
|
||||
ExportMbox(Option<melib::backends::mbox::MboxFormat>, PathBuf),
|
||||
Delete,
|
||||
OpenInNewTab,
|
||||
Tag(TagAction),
|
||||
|
|
|
@ -31,6 +31,7 @@ use indexmap::IndexSet;
|
|||
use nix::sys::wait::WaitStatus;
|
||||
use std::convert::TryInto;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
|
|
@ -342,6 +342,87 @@ pub trait MailListingTrait: ListingTrait {
|
|||
}
|
||||
}
|
||||
}
|
||||
ListingAction::ExportMbox(format, ref path) => {
|
||||
use futures::future::try_join_all;
|
||||
use std::future::Future;
|
||||
use std::io::Write;
|
||||
use std::pin::Pin;
|
||||
|
||||
let futures: Result<Vec<_>> = envs_to_set
|
||||
.iter()
|
||||
.map(|&env_hash| account.operation(env_hash).and_then(|mut op| op.as_bytes()))
|
||||
.collect::<Result<Vec<_>>>();
|
||||
let path_ = path.to_path_buf();
|
||||
let format = format.clone().unwrap_or_default();
|
||||
let collection = account.collection.clone();
|
||||
let (sender, mut receiver) = crate::jobs::oneshot::channel();
|
||||
let fut: Pin<Box<dyn Future<Output = Result<()>> + Send + 'static>> =
|
||||
Box::pin(async move {
|
||||
let cl = async move {
|
||||
let bytes: Vec<Vec<u8>> = try_join_all(futures?).await?;
|
||||
let envs: Vec<_> = envs_to_set
|
||||
.iter()
|
||||
.map(|&env_hash| collection.get_env(env_hash))
|
||||
.collect();
|
||||
let mut file = std::io::BufWriter::new(std::fs::File::create(&path_)?);
|
||||
let mut iter = envs.iter().zip(bytes.into_iter());
|
||||
if let Some((env, ref bytes)) = iter.next() {
|
||||
format.append(
|
||||
&mut file,
|
||||
bytes.as_slice(),
|
||||
env.from().get(0),
|
||||
Some(env.date()),
|
||||
true,
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
for (env, bytes) in iter {
|
||||
format.append(
|
||||
&mut file,
|
||||
bytes.as_slice(),
|
||||
env.from().get(0),
|
||||
Some(env.date()),
|
||||
false,
|
||||
false,
|
||||
)?;
|
||||
}
|
||||
file.flush()?;
|
||||
Ok(())
|
||||
};
|
||||
let r: Result<()> = cl.await;
|
||||
let _ = sender.send(r);
|
||||
Ok(())
|
||||
});
|
||||
let handle = account.job_executor.spawn_blocking(fut);
|
||||
let path = path.to_path_buf();
|
||||
account.insert_job(
|
||||
handle.job_id,
|
||||
JobRequest::Generic {
|
||||
name: "exporting mbox".into(),
|
||||
handle,
|
||||
on_finish: Some(CallbackFn(Box::new(move |context: &mut Context| {
|
||||
context.replies.push_back(match receiver.try_recv() {
|
||||
Err(_) | Ok(None) => UIEvent::Notification(
|
||||
Some("Could not export mbox".to_string()),
|
||||
"Job was canceled.".to_string(),
|
||||
Some(NotificationType::Info),
|
||||
),
|
||||
Ok(Some(Err(err))) => UIEvent::Notification(
|
||||
Some("Could not export mbox".to_string()),
|
||||
err.to_string(),
|
||||
Some(NotificationType::Error(err.kind)),
|
||||
),
|
||||
Ok(Some(Ok(()))) => UIEvent::Notification(
|
||||
Some("Succesfully exported mbox".to_string()),
|
||||
format!("Wrote to file {}", path.display()),
|
||||
Some(NotificationType::Info),
|
||||
),
|
||||
});
|
||||
}))),
|
||||
logging_level: melib::LoggingLevel::INFO,
|
||||
},
|
||||
);
|
||||
}
|
||||
ListingAction::MoveToOtherAccount(ref _account_name, ref _mailbox_path) => {
|
||||
context
|
||||
.replies
|
||||
|
@ -967,6 +1048,7 @@ impl Component for Listing {
|
|||
| Action::Listing(a @ ListingAction::MoveTo(_))
|
||||
| Action::Listing(a @ ListingAction::CopyToOtherAccount(_, _))
|
||||
| Action::Listing(a @ ListingAction::MoveToOtherAccount(_, _))
|
||||
| Action::Listing(a @ ListingAction::ExportMbox(_, _))
|
||||
| Action::Listing(a @ ListingAction::Tag(_)) => {
|
||||
let focused = self.component.get_focused_items(context);
|
||||
self.component.perform_action(context, focused, a);
|
||||
|
|
|
@ -895,9 +895,9 @@ impl CompactListing {
|
|||
let thread = threads.thread_ref(hash);
|
||||
let mut tags = String::new();
|
||||
let mut colors: SmallVec<[_; 8]> = SmallVec::new();
|
||||
let backend_lck = context.accounts[&self.cursor_pos.0].backend.read().unwrap();
|
||||
if let Some(t) = backend_lck.tags() {
|
||||
let tags_lck = t.read().unwrap();
|
||||
let account = &context.accounts[&self.cursor_pos.0];
|
||||
if account.backend_capabilities.supports_tags {
|
||||
let tags_lck = account.collection.tag_index.read().unwrap();
|
||||
for t in e.labels().iter() {
|
||||
if mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
|
|
|
@ -908,9 +908,9 @@ impl ConversationsListing {
|
|||
let thread = threads.thread_ref(hash);
|
||||
let mut tags = String::new();
|
||||
let mut colors = SmallVec::new();
|
||||
let backend_lck = context.accounts[&self.cursor_pos.0].backend.read().unwrap();
|
||||
if let Some(t) = backend_lck.tags() {
|
||||
let tags_lck = t.read().unwrap();
|
||||
let account = &context.accounts[&self.cursor_pos.0];
|
||||
if account.backend_capabilities.supports_tags {
|
||||
let tags_lck = account.collection.tag_index.read().unwrap();
|
||||
for t in e.labels().iter() {
|
||||
if mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
|
|
|
@ -732,9 +732,9 @@ impl PlainListing {
|
|||
fn make_entry_string(&self, e: EnvelopeRef, context: &Context) -> EntryStrings {
|
||||
let mut tags = String::new();
|
||||
let mut colors = SmallVec::new();
|
||||
let backend_lck = context.accounts[&self.cursor_pos.0].backend.read().unwrap();
|
||||
if let Some(t) = backend_lck.tags() {
|
||||
let tags_lck = t.read().unwrap();
|
||||
let account = &context.accounts[&self.cursor_pos.0];
|
||||
if account.backend_capabilities.supports_tags {
|
||||
let tags_lck = account.collection.tag_index.read().unwrap();
|
||||
for t in e.labels().iter() {
|
||||
if mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
|
|
|
@ -839,9 +839,9 @@ impl ThreadListing {
|
|||
fn make_entry_string(&self, e: &Envelope, context: &Context) -> EntryStrings {
|
||||
let mut tags = String::new();
|
||||
let mut colors: SmallVec<[_; 8]> = SmallVec::new();
|
||||
let backend_lck = context.accounts[&self.cursor_pos.0].backend.read().unwrap();
|
||||
if let Some(t) = backend_lck.tags() {
|
||||
let tags_lck = t.read().unwrap();
|
||||
let account = &context.accounts[&self.cursor_pos.0];
|
||||
if account.backend_capabilities.supports_tags {
|
||||
let tags_lck = account.collection.tag_index.read().unwrap();
|
||||
for t in e.labels().iter() {
|
||||
if mailbox_settings!(
|
||||
context[self.cursor_pos.0][&self.cursor_pos.1]
|
||||
|
|
|
@ -40,9 +40,10 @@ use std::collections::{HashMap, HashSet};
|
|||
use crate::types::UIEvent::{self, EnvelopeRemove, EnvelopeRename, EnvelopeUpdate, Notification};
|
||||
use crate::{StatusEvent, ThreadEvent};
|
||||
use crossbeam::Sender;
|
||||
use futures::future::FutureExt;
|
||||
pub use futures::stream::Stream;
|
||||
use futures::stream::StreamExt;
|
||||
use futures::{
|
||||
future::FutureExt,
|
||||
stream::{Stream, StreamExt},
|
||||
};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::VecDeque;
|
||||
use std::convert::TryFrom;
|
||||
|
@ -517,7 +518,7 @@ impl Account {
|
|||
tree: Default::default(),
|
||||
address_book,
|
||||
sent_mailbox: Default::default(),
|
||||
collection: Default::default(),
|
||||
collection: backend.collection(),
|
||||
settings,
|
||||
sender,
|
||||
job_executor,
|
||||
|
|
|
@ -46,7 +46,7 @@ pub struct PluginBackend {
|
|||
plugin: Plugin,
|
||||
child: std::process::Child,
|
||||
channel: Arc<Mutex<RpcChannel>>,
|
||||
tag_index: Option<Arc<RwLock<BTreeMap<u64, String>>>>,
|
||||
collection: melib::Collection,
|
||||
is_online: Arc<Mutex<(std::time::Instant, Result<()>)>>,
|
||||
}
|
||||
|
||||
|
@ -200,7 +200,6 @@ impl MailBackend for PluginBackend {
|
|||
Ok(Box::new(PluginOp {
|
||||
hash,
|
||||
channel: self.channel.clone(),
|
||||
tag_index: self.tag_index.clone(),
|
||||
bytes: None,
|
||||
}))
|
||||
}
|
||||
|
@ -219,8 +218,8 @@ impl MailBackend for PluginBackend {
|
|||
) -> ResultFuture<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
|
||||
Err(MeliError::new("Unimplemented."))
|
||||
}
|
||||
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
|
||||
self.tag_index.clone()
|
||||
fn collection(&self) -> melib::Collection {
|
||||
self.collection.clone()
|
||||
}
|
||||
fn as_any(&self) -> &dyn ::std::any::Any {
|
||||
self
|
||||
|
@ -257,7 +256,7 @@ impl PluginBackend {
|
|||
child,
|
||||
plugin,
|
||||
channel: Arc::new(Mutex::new(channel)),
|
||||
tag_index: None,
|
||||
collection: Default::default(),
|
||||
is_online: Arc::new(Mutex::new((now, Err(MeliError::new("Unitialized"))))),
|
||||
}))
|
||||
}
|
||||
|
@ -285,7 +284,6 @@ impl PluginBackend {
|
|||
struct PluginOp {
|
||||
hash: EnvelopeHash,
|
||||
channel: Arc<Mutex<RpcChannel>>,
|
||||
tag_index: Option<Arc<RwLock<BTreeMap<u64, String>>>>,
|
||||
bytes: Option<String>,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue