2017-09-14 18:08:14 +03:00
|
|
|
/*
|
|
|
|
* meli - accounts module.
|
|
|
|
*
|
|
|
|
* Copyright 2017 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/>.
|
|
|
|
*/
|
|
|
|
|
2023-07-01 16:20:59 +03:00
|
|
|
//! Account management from user configuration.
|
2018-08-06 22:20:34 +03:00
|
|
|
|
2023-04-30 19:39:41 +03:00
|
|
|
use std::{
|
|
|
|
collections::{BTreeMap, HashMap, HashSet, VecDeque},
|
|
|
|
convert::TryFrom,
|
|
|
|
fs,
|
|
|
|
future::Future,
|
|
|
|
io,
|
|
|
|
ops::{Index, IndexMut},
|
|
|
|
os::unix::fs::PermissionsExt,
|
|
|
|
pin::Pin,
|
|
|
|
result,
|
|
|
|
sync::{Arc, RwLock},
|
2023-06-19 00:13:53 +03:00
|
|
|
time::Duration,
|
2023-04-30 19:39:41 +03:00
|
|
|
};
|
2019-02-15 09:06:42 +02:00
|
|
|
|
2024-03-16 12:48:57 +02:00
|
|
|
use futures::{future::FutureExt, stream::StreamExt};
|
2023-04-30 19:39:41 +03:00
|
|
|
use indexmap::IndexMap;
|
|
|
|
use melib::{
|
|
|
|
backends::*,
|
|
|
|
email::*,
|
|
|
|
error::{Error, ErrorKind, Result},
|
2023-05-01 16:22:35 +03:00
|
|
|
log,
|
2023-12-26 16:47:38 +02:00
|
|
|
text::GlobMatch,
|
2023-08-21 12:38:53 +03:00
|
|
|
thread::Threads,
|
|
|
|
AddressBook, Collection, LogLevel, SortField, SortOrder,
|
2023-04-30 19:39:41 +03:00
|
|
|
};
|
|
|
|
use smallvec::SmallVec;
|
|
|
|
|
2023-07-16 11:37:09 +03:00
|
|
|
#[cfg(feature = "sqlite3")]
|
|
|
|
use crate::command::actions::AccountAction;
|
2023-04-30 19:39:41 +03:00
|
|
|
use crate::{
|
2023-09-05 15:44:43 +03:00
|
|
|
conf::{AccountConf, FileMailboxConf},
|
2023-06-14 12:24:20 +03:00
|
|
|
jobs::{JobId, JoinHandle},
|
2024-03-23 10:43:58 +02:00
|
|
|
types::{
|
|
|
|
ForkType,
|
|
|
|
UIEvent::{self, EnvelopeRemove, EnvelopeRename, EnvelopeUpdate, Notification},
|
|
|
|
},
|
2023-06-14 12:24:20 +03:00
|
|
|
MainLoopHandler, StatusEvent, ThreadEvent,
|
2023-04-30 19:39:41 +03:00
|
|
|
};
|
2018-08-03 13:46:08 +03:00
|
|
|
|
2023-11-24 12:58:21 +02:00
|
|
|
mod backend_ops;
|
2024-03-16 12:48:57 +02:00
|
|
|
mod jobs;
|
|
|
|
mod mailbox;
|
|
|
|
|
|
|
|
pub use jobs::*;
|
|
|
|
pub use mailbox::*;
|
2023-11-24 12:58:21 +02:00
|
|
|
|
2020-07-05 13:22:48 +03:00
|
|
|
#[macro_export]
|
|
|
|
macro_rules! try_recv_timeout {
|
|
|
|
($oneshot:expr) => {{
|
|
|
|
const _3_MS: std::time::Duration = std::time::Duration::from_millis(95);
|
|
|
|
let now = std::time::Instant::now();
|
|
|
|
let mut res = Ok(None);
|
|
|
|
while now + _3_MS >= std::time::Instant::now() {
|
2022-12-08 22:20:05 +02:00
|
|
|
res = $oneshot.try_recv().map_err(|_| Error::new("canceled"));
|
2020-07-05 13:22:48 +03:00
|
|
|
if res.as_ref().map(|r| r.is_some()).unwrap_or(false) || res.is_err() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
res
|
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
2024-03-16 12:48:57 +02:00
|
|
|
#[macro_export]
|
2023-06-18 22:28:40 +03:00
|
|
|
macro_rules! is_variant {
|
|
|
|
($n:ident, $($var:tt)+) => {
|
|
|
|
#[inline]
|
|
|
|
pub fn $n(&self) -> bool {
|
|
|
|
matches!(self, Self::$($var)*)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-12-09 18:47:16 +02:00
|
|
|
#[derive(Clone, Debug, Default)]
|
2023-06-19 00:13:53 +03:00
|
|
|
pub enum IsOnline {
|
|
|
|
#[default]
|
|
|
|
Uninit,
|
|
|
|
True,
|
|
|
|
Err {
|
|
|
|
value: Error,
|
|
|
|
retries: u64,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IsOnline {
|
|
|
|
is_variant! { is_uninit, Uninit }
|
|
|
|
is_variant! { is_true, True }
|
|
|
|
is_variant! { is_err, Err { .. } }
|
|
|
|
|
|
|
|
fn set_err(&mut self, err: Error) {
|
|
|
|
if let Self::Err { ref mut value, .. } = self {
|
|
|
|
*value = err;
|
|
|
|
} else {
|
|
|
|
*self = Self::Err {
|
|
|
|
value: err,
|
|
|
|
retries: 1,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
2024-03-16 18:10:37 +02:00
|
|
|
|
|
|
|
pub fn is_recoverable(err: &Error) -> bool {
|
|
|
|
!(err.kind.is_authentication()
|
|
|
|
|| err.kind.is_configuration()
|
|
|
|
|| err.kind.is_bug()
|
|
|
|
|| err.kind.is_external()
|
|
|
|
|| (err.kind.is_network() && !err.kind.is_network_down())
|
|
|
|
|| err.kind.is_not_implemented()
|
|
|
|
|| err.kind.is_not_supported()
|
|
|
|
|| err.kind.is_protocol_error()
|
|
|
|
|| err.kind.is_protocol_not_supported()
|
|
|
|
|| err.kind.is_value_error())
|
|
|
|
}
|
2023-06-19 00:13:53 +03:00
|
|
|
}
|
|
|
|
|
2017-09-14 18:08:14 +03:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Account {
|
2022-11-25 15:15:02 +02:00
|
|
|
pub name: String,
|
|
|
|
pub hash: AccountHash,
|
2023-06-19 00:13:53 +03:00
|
|
|
pub is_online: IsOnline,
|
2022-11-25 15:15:02 +02:00
|
|
|
pub mailbox_entries: IndexMap<MailboxHash, MailboxEntry>,
|
|
|
|
pub mailboxes_order: Vec<MailboxHash>,
|
|
|
|
pub tree: Vec<MailboxNode>,
|
|
|
|
pub collection: Collection,
|
|
|
|
pub address_book: AddressBook,
|
|
|
|
pub settings: AccountConf,
|
|
|
|
pub backend: Arc<RwLock<Box<dyn MailBackend>>>,
|
2019-05-26 02:41:29 +03:00
|
|
|
|
2023-06-14 12:24:20 +03:00
|
|
|
pub main_loop_handler: MainLoopHandler,
|
2020-06-30 11:40:26 +03:00
|
|
|
pub active_jobs: HashMap<JobId, JobRequest>,
|
2020-07-24 21:48:29 +03:00
|
|
|
pub active_job_instants: BTreeMap<std::time::Instant, JobId>,
|
2022-11-25 15:15:02 +02:00
|
|
|
pub event_queue: VecDeque<(MailboxHash, RefreshEvent)>,
|
2020-07-25 17:53:04 +03:00
|
|
|
pub backend_capabilities: MailBackendCapabilities,
|
2017-09-14 18:08:14 +03:00
|
|
|
}
|
|
|
|
|
2019-02-15 09:06:42 +02:00
|
|
|
impl Drop for Account {
|
|
|
|
fn drop(&mut self) {
|
2019-11-22 13:59:00 +02:00
|
|
|
if let Ok(data_dir) = xdg::BaseDirectories::with_profile("meli", &self.name) {
|
|
|
|
if let Ok(data) = data_dir.place_data_file("addressbook") {
|
|
|
|
/* place result in cache directory */
|
|
|
|
let f = match fs::File::create(data) {
|
|
|
|
Ok(f) => f,
|
|
|
|
Err(e) => {
|
|
|
|
eprintln!("{}", e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
2019-11-24 17:00:55 +02:00
|
|
|
let metadata = f.metadata().unwrap();
|
|
|
|
let mut permissions = metadata.permissions();
|
|
|
|
|
|
|
|
permissions.set_mode(0o600); // Read/write for owner only.
|
|
|
|
f.set_permissions(permissions).unwrap();
|
2019-11-22 13:59:00 +02:00
|
|
|
let writer = io::BufWriter::new(f);
|
|
|
|
if let Err(err) = serde_json::to_writer(writer, &self.address_book) {
|
|
|
|
eprintln!("{}", err);
|
|
|
|
};
|
2019-02-15 09:06:42 +02:00
|
|
|
};
|
2020-02-28 09:16:50 +02:00
|
|
|
/*
|
2019-11-22 13:59:00 +02:00
|
|
|
if let Ok(data) = data_dir.place_data_file("mailbox") {
|
|
|
|
/* place result in cache directory */
|
|
|
|
let f = match fs::File::create(data) {
|
|
|
|
Ok(f) => f,
|
|
|
|
Err(e) => {
|
|
|
|
eprintln!("{}", e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
2019-11-24 17:00:55 +02:00
|
|
|
let metadata = f.metadata().unwrap();
|
|
|
|
let mut permissions = metadata.permissions();
|
|
|
|
|
|
|
|
permissions.set_mode(0o600); // Read/write for owner only.
|
|
|
|
f.set_permissions(permissions).unwrap();
|
2019-11-22 13:59:00 +02:00
|
|
|
let writer = io::BufWriter::new(f);
|
2020-11-09 00:40:32 +02:00
|
|
|
if let Err(err) = bincode::Options::serialize_into(
|
|
|
|
bincode::config::DefaultOptions::new(),
|
|
|
|
writer,
|
|
|
|
&self.collection,
|
|
|
|
) {
|
2019-11-22 13:59:00 +02:00
|
|
|
eprintln!("{}", err);
|
|
|
|
};
|
2019-05-14 21:47:47 +03:00
|
|
|
};
|
2020-02-28 09:16:50 +02:00
|
|
|
*/
|
2019-11-22 13:59:00 +02:00
|
|
|
}
|
2019-02-15 09:06:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-14 18:08:14 +03:00
|
|
|
impl Account {
|
2019-09-20 09:10:33 +03:00
|
|
|
pub fn new(
|
2020-06-08 22:08:10 +03:00
|
|
|
hash: AccountHash,
|
2019-09-20 09:10:33 +03:00
|
|
|
name: String,
|
2019-11-11 18:01:01 +02:00
|
|
|
mut settings: AccountConf,
|
2019-09-20 09:10:33 +03:00
|
|
|
map: &Backends,
|
2023-06-14 12:24:20 +03:00
|
|
|
main_loop_handler: MainLoopHandler,
|
2020-08-20 01:55:24 +03:00
|
|
|
event_consumer: BackendEventConsumer,
|
2019-11-16 00:33:22 +02:00
|
|
|
) -> Result<Self> {
|
2019-09-06 12:48:17 +03:00
|
|
|
let s = settings.clone();
|
2023-05-16 19:22:13 +03:00
|
|
|
let backend = map.get(&settings.account().format)(
|
2019-09-06 12:48:17 +03:00
|
|
|
settings.account(),
|
|
|
|
Box::new(move |path: &str| {
|
2023-07-20 00:19:42 +03:00
|
|
|
// disjoint-capture-in-closures
|
|
|
|
let _ = &s;
|
2020-02-26 10:54:10 +02:00
|
|
|
s.account.subscribed_mailboxes.is_empty()
|
|
|
|
|| (s.mailbox_confs.contains_key(path)
|
|
|
|
&& s.mailbox_confs[path].mailbox_conf().subscribe.is_true())
|
2019-11-23 17:56:38 +02:00
|
|
|
|| s.account
|
2020-02-26 10:54:10 +02:00
|
|
|
.subscribed_mailboxes
|
2019-11-23 17:56:38 +02:00
|
|
|
.iter()
|
|
|
|
.any(|m| path.matches_glob(m))
|
2019-09-06 12:48:17 +03:00
|
|
|
}),
|
2020-08-20 01:55:24 +03:00
|
|
|
event_consumer,
|
2019-11-16 00:33:22 +02:00
|
|
|
)?;
|
2019-10-24 20:30:17 +03:00
|
|
|
|
|
|
|
let data_dir = xdg::BaseDirectories::with_profile("meli", &name).unwrap();
|
2022-08-25 15:17:18 +03:00
|
|
|
let mut address_book = AddressBook::with_account(settings.account());
|
2019-11-27 01:39:06 +02:00
|
|
|
|
|
|
|
if let Ok(data) = data_dir.place_data_file("addressbook") {
|
2019-10-24 20:30:17 +03:00
|
|
|
if data.exists() {
|
|
|
|
let reader = io::BufReader::new(fs::File::open(data).unwrap());
|
|
|
|
let result: result::Result<AddressBook, _> = serde_json::from_reader(reader);
|
|
|
|
if let Ok(data_t) = result {
|
2019-11-27 01:39:06 +02:00
|
|
|
for (id, c) in data_t.cards {
|
|
|
|
if !address_book.card_exists(id) && !c.external_resource() {
|
|
|
|
address_book.add_card(c);
|
|
|
|
}
|
|
|
|
}
|
2019-10-24 20:30:17 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2019-11-27 01:39:06 +02:00
|
|
|
|
2020-09-18 21:38:50 +03:00
|
|
|
if settings.conf.search_backend == crate::conf::SearchBackend::Auto {
|
|
|
|
if backend.capabilities().supports_search {
|
|
|
|
settings.conf.search_backend = crate::conf::SearchBackend::None;
|
|
|
|
} else {
|
|
|
|
#[cfg(feature = "sqlite3")]
|
|
|
|
{
|
|
|
|
settings.conf.search_backend = crate::conf::SearchBackend::Sqlite3;
|
|
|
|
}
|
|
|
|
#[cfg(not(feature = "sqlite3"))]
|
|
|
|
{
|
|
|
|
settings.conf.search_backend = crate::conf::SearchBackend::None;
|
|
|
|
}
|
|
|
|
}
|
2019-11-11 18:01:01 +02:00
|
|
|
}
|
|
|
|
|
2020-06-27 21:40:46 +03:00
|
|
|
let mut active_jobs = HashMap::default();
|
2020-07-24 21:48:29 +03:00
|
|
|
let mut active_job_instants = BTreeMap::default();
|
2020-08-20 17:37:19 +03:00
|
|
|
if let Ok(mailboxes_job) = backend.mailboxes() {
|
|
|
|
if let Ok(online_job) = backend.is_online() {
|
2020-10-09 19:34:55 +03:00
|
|
|
let handle = if backend.capabilities().is_async {
|
2023-06-14 12:24:20 +03:00
|
|
|
main_loop_handler
|
|
|
|
.job_executor
|
2023-07-14 00:23:24 +03:00
|
|
|
.spawn_specialized("mailboxes".into(), online_job.then(|_| mailboxes_job))
|
2020-08-20 17:37:19 +03:00
|
|
|
} else {
|
2023-06-14 12:24:20 +03:00
|
|
|
main_loop_handler
|
|
|
|
.job_executor
|
2023-07-14 00:23:24 +03:00
|
|
|
.spawn_blocking("mailboxes".into(), online_job.then(|_| mailboxes_job))
|
2020-08-20 17:37:19 +03:00
|
|
|
};
|
2020-10-09 19:34:55 +03:00
|
|
|
let job_id = handle.job_id;
|
|
|
|
active_jobs.insert(job_id, JobRequest::Mailboxes { handle });
|
2020-08-20 17:37:19 +03:00
|
|
|
active_job_instants.insert(std::time::Instant::now(), job_id);
|
2023-06-14 12:24:20 +03:00
|
|
|
main_loop_handler.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
|
|
|
StatusEvent::NewJob(job_id),
|
|
|
|
)));
|
2020-06-30 11:40:26 +03:00
|
|
|
}
|
2020-06-27 21:40:46 +03:00
|
|
|
}
|
2022-09-11 17:42:22 +03:00
|
|
|
|
|
|
|
#[cfg(feature = "sqlite3")]
|
|
|
|
if settings.conf.search_backend == crate::conf::SearchBackend::Sqlite3 {
|
2024-02-23 14:48:17 +02:00
|
|
|
let db_path = match crate::sqlite3::AccountCache::db_path(&name) {
|
2022-09-11 17:42:22 +03:00
|
|
|
Err(err) => {
|
2023-06-14 12:24:20 +03:00
|
|
|
main_loop_handler.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
|
|
|
StatusEvent::DisplayMessage(format!(
|
2023-06-16 20:20:12 +03:00
|
|
|
"Error with setting up an sqlite3 search database for account `{}`: {}",
|
2023-06-14 12:24:20 +03:00
|
|
|
name, err
|
|
|
|
)),
|
|
|
|
)));
|
2022-09-11 17:42:22 +03:00
|
|
|
None
|
|
|
|
}
|
2024-03-03 14:01:02 +02:00
|
|
|
Ok(path) => path,
|
2022-09-11 17:42:22 +03:00
|
|
|
};
|
|
|
|
if let Some(db_path) = db_path {
|
|
|
|
if !db_path.exists() {
|
2023-06-05 20:05:43 +03:00
|
|
|
log::info!(
|
|
|
|
"An sqlite3 search database for account `{}` seems to be missing, a new \
|
|
|
|
one will be created.",
|
|
|
|
name
|
|
|
|
);
|
2023-06-14 12:24:20 +03:00
|
|
|
main_loop_handler.send(ThreadEvent::UIEvent(UIEvent::Action(
|
|
|
|
(name.clone(), AccountAction::ReIndex).into(),
|
|
|
|
)));
|
2022-09-11 17:42:22 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-01 15:23:22 +02:00
|
|
|
Ok(Self {
|
2020-06-08 22:08:10 +03:00
|
|
|
hash,
|
2019-10-24 20:30:17 +03:00
|
|
|
name,
|
2020-07-25 17:53:04 +03:00
|
|
|
is_online: if !backend.capabilities().is_remote {
|
2023-06-19 00:13:53 +03:00
|
|
|
IsOnline::True
|
2020-07-08 00:26:40 +03:00
|
|
|
} else {
|
2023-06-19 00:13:53 +03:00
|
|
|
IsOnline::Uninit
|
2020-07-08 00:26:40 +03:00
|
|
|
},
|
2020-02-26 10:54:10 +02:00
|
|
|
mailbox_entries: Default::default(),
|
|
|
|
mailboxes_order: Default::default(),
|
2019-10-24 20:30:17 +03:00
|
|
|
tree: Default::default(),
|
|
|
|
address_book,
|
2020-08-10 14:24:21 +03:00
|
|
|
collection: backend.collection(),
|
2019-10-24 20:30:17 +03:00
|
|
|
settings,
|
2023-06-14 12:24:20 +03:00
|
|
|
main_loop_handler,
|
2020-06-27 21:40:46 +03:00
|
|
|
active_jobs,
|
2020-07-24 21:48:29 +03:00
|
|
|
active_job_instants,
|
2019-10-24 20:30:17 +03:00
|
|
|
event_queue: VecDeque::with_capacity(8),
|
2020-07-25 17:53:04 +03:00
|
|
|
backend_capabilities: backend.capabilities(),
|
2020-07-05 19:17:28 +03:00
|
|
|
backend: Arc::new(RwLock::new(backend)),
|
2020-08-20 17:37:19 +03:00
|
|
|
})
|
2019-10-24 20:30:17 +03:00
|
|
|
}
|
2019-11-12 22:14:44 +02:00
|
|
|
|
2020-08-20 17:37:19 +03:00
|
|
|
fn init(&mut self, mut ref_mailboxes: HashMap<MailboxHash, Mailbox>) -> Result<()> {
|
2020-08-01 12:36:47 +03:00
|
|
|
self.backend_capabilities = self.backend.read().unwrap().capabilities();
|
2020-08-17 15:31:30 +03:00
|
|
|
let mut mailbox_entries: IndexMap<MailboxHash, MailboxEntry> =
|
|
|
|
IndexMap::with_capacity_and_hasher(ref_mailboxes.len(), Default::default());
|
2020-02-26 10:54:10 +02:00
|
|
|
let mut mailboxes_order: Vec<MailboxHash> = Vec::with_capacity(ref_mailboxes.len());
|
|
|
|
|
2023-04-30 19:39:41 +03:00
|
|
|
/* Keep track of which mailbox config values we encounter in the actual
|
|
|
|
* mailboxes returned by the backend. For each of the actual
|
|
|
|
* mailboxes, delete the key from the hash set. If any are left, they
|
|
|
|
* are misconfigurations (eg misspelling) and a warning is shown to the
|
2020-05-19 15:00:26 +03:00
|
|
|
* user */
|
|
|
|
let mut mailbox_conf_hash_set = self
|
|
|
|
.settings
|
|
|
|
.mailbox_confs
|
|
|
|
.keys()
|
|
|
|
.cloned()
|
|
|
|
.collect::<HashSet<String>>();
|
2024-03-16 15:01:28 +02:00
|
|
|
let mut default_mailbox = self
|
|
|
|
.settings
|
|
|
|
.conf
|
|
|
|
.default_mailbox
|
|
|
|
.clone()
|
|
|
|
.into_iter()
|
|
|
|
.collect::<HashSet<String>>();
|
2020-02-26 10:54:10 +02:00
|
|
|
for f in ref_mailboxes.values_mut() {
|
|
|
|
if let Some(conf) = self.settings.mailbox_confs.get_mut(f.path()) {
|
2020-05-19 15:00:26 +03:00
|
|
|
mailbox_conf_hash_set.remove(f.path());
|
2024-03-16 15:01:28 +02:00
|
|
|
if default_mailbox.remove(f.path()) {
|
|
|
|
self.settings.default_mailbox = Some(f.hash());
|
|
|
|
}
|
2020-02-26 10:54:10 +02:00
|
|
|
conf.mailbox_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
|
2019-12-17 14:12:41 +02:00
|
|
|
Some(f.special_usage())
|
|
|
|
} else {
|
|
|
|
let tmp = SpecialUsageMailbox::detect_usage(f.name());
|
2020-12-25 06:09:46 +02:00
|
|
|
if let Some(tmp) = tmp.filter(|&v| v != SpecialUsageMailbox::Normal) {
|
|
|
|
let _ = f.set_special_usage(tmp);
|
2019-12-17 14:12:41 +02:00
|
|
|
}
|
|
|
|
tmp
|
|
|
|
};
|
2020-02-26 10:54:10 +02:00
|
|
|
match conf.mailbox_conf.usage {
|
2019-12-11 00:15:36 +02:00
|
|
|
Some(SpecialUsageMailbox::Sent) => {
|
2024-03-16 14:57:48 +02:00
|
|
|
self.settings.sent_mailbox = Some(f.hash());
|
2019-11-23 17:56:38 +02:00
|
|
|
}
|
2019-12-17 14:12:41 +02:00
|
|
|
None => {
|
|
|
|
if f.special_usage() == SpecialUsageMailbox::Sent {
|
2024-03-16 14:57:48 +02:00
|
|
|
self.settings.sent_mailbox = Some(f.hash());
|
2019-12-17 14:12:41 +02:00
|
|
|
}
|
|
|
|
}
|
2019-11-23 17:56:38 +02:00
|
|
|
_ => {}
|
2019-08-23 21:58:41 +03:00
|
|
|
}
|
2020-02-26 10:54:10 +02:00
|
|
|
mailbox_entries.insert(
|
2020-02-10 02:11:07 +02:00
|
|
|
f.hash(),
|
2023-04-09 00:03:20 +03:00
|
|
|
MailboxEntry::new(
|
|
|
|
MailboxStatus::None,
|
|
|
|
f.path().to_string(),
|
|
|
|
f.clone(),
|
|
|
|
conf.clone(),
|
|
|
|
),
|
2020-02-10 02:11:07 +02:00
|
|
|
);
|
2019-11-23 17:56:38 +02:00
|
|
|
} else {
|
2020-02-26 10:54:10 +02:00
|
|
|
let mut new = FileMailboxConf::default();
|
|
|
|
new.mailbox_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
|
2019-12-17 14:12:41 +02:00
|
|
|
Some(f.special_usage())
|
|
|
|
} else {
|
|
|
|
let tmp = SpecialUsageMailbox::detect_usage(f.name());
|
2020-12-25 06:09:46 +02:00
|
|
|
if let Some(tmp) = tmp.filter(|&v| v != SpecialUsageMailbox::Normal) {
|
|
|
|
let _ = f.set_special_usage(tmp);
|
2019-12-17 14:12:41 +02:00
|
|
|
}
|
|
|
|
tmp
|
|
|
|
};
|
2020-02-26 10:54:10 +02:00
|
|
|
if new.mailbox_conf.usage == Some(SpecialUsageMailbox::Sent) {
|
2024-03-16 14:57:48 +02:00
|
|
|
self.settings.sent_mailbox = Some(f.hash());
|
2020-02-12 18:56:05 +02:00
|
|
|
}
|
2019-12-17 14:12:41 +02:00
|
|
|
|
2020-02-26 10:54:10 +02:00
|
|
|
mailbox_entries.insert(
|
2020-02-10 02:11:07 +02:00
|
|
|
f.hash(),
|
2023-04-09 00:03:20 +03:00
|
|
|
MailboxEntry::new(MailboxStatus::None, f.path().to_string(), f.clone(), new),
|
2020-02-10 02:11:07 +02:00
|
|
|
);
|
2019-08-23 21:58:41 +03:00
|
|
|
}
|
2019-04-10 18:58:09 +03:00
|
|
|
}
|
2019-05-13 21:08:18 +03:00
|
|
|
|
2020-05-19 15:00:26 +03:00
|
|
|
for missing_mailbox in &mailbox_conf_hash_set {
|
2023-05-01 16:22:35 +03:00
|
|
|
log::warn!(
|
|
|
|
"Account `{}` mailbox `{}` configured but not present in account's mailboxes. Is \
|
|
|
|
it misspelled?",
|
|
|
|
&self.name,
|
|
|
|
missing_mailbox,
|
2020-05-19 15:00:26 +03:00
|
|
|
);
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
2020-05-19 15:00:26 +03:00
|
|
|
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
2023-04-30 19:39:41 +03:00
|
|
|
StatusEvent::DisplayMessage(format!(
|
|
|
|
"Account `{}` mailbox `{}` configured but not present in account's \
|
|
|
|
mailboxes. Is it misspelled?",
|
|
|
|
&self.name, missing_mailbox,
|
|
|
|
)),
|
2023-06-14 12:24:20 +03:00
|
|
|
)));
|
2020-05-19 15:00:26 +03:00
|
|
|
}
|
|
|
|
if !mailbox_conf_hash_set.is_empty() {
|
|
|
|
let mut mailbox_comma_sep_list_string = mailbox_entries
|
|
|
|
.values()
|
|
|
|
.map(|e| e.name.as_str())
|
|
|
|
.fold(String::new(), |mut acc, el| {
|
|
|
|
acc.push('`');
|
|
|
|
acc.push_str(el);
|
|
|
|
acc.push('`');
|
|
|
|
acc.push_str(", ");
|
|
|
|
acc
|
|
|
|
});
|
2022-10-04 15:42:24 +03:00
|
|
|
mailbox_comma_sep_list_string
|
|
|
|
.drain(mailbox_comma_sep_list_string.len().saturating_sub(2)..);
|
2023-05-01 16:22:35 +03:00
|
|
|
log::warn!(
|
|
|
|
"Account `{}` has the following mailboxes: [{}]",
|
|
|
|
&self.name,
|
|
|
|
mailbox_comma_sep_list_string,
|
2020-05-19 15:00:26 +03:00
|
|
|
);
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
2022-10-04 15:42:24 +03:00
|
|
|
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
|
|
|
StatusEvent::DisplayMessage(format!(
|
|
|
|
"Account `{}` has the following mailboxes: [{}]",
|
|
|
|
&self.name, mailbox_comma_sep_list_string,
|
|
|
|
)),
|
2023-06-14 12:24:20 +03:00
|
|
|
)));
|
2020-05-19 15:00:26 +03:00
|
|
|
}
|
|
|
|
|
2024-03-16 15:01:28 +02:00
|
|
|
match self.settings.conf.default_mailbox {
|
|
|
|
Some(ref v) if !default_mailbox.is_empty() => {
|
|
|
|
let err = Error::new(format!(
|
|
|
|
"Account `{}` has default mailbox set as `{}` but it doesn't exist.",
|
|
|
|
&self.name, v
|
|
|
|
))
|
|
|
|
.set_kind(ErrorKind::Configuration);
|
|
|
|
self.is_online.set_err(err.clone());
|
|
|
|
self.main_loop_handler
|
|
|
|
.send(ThreadEvent::UIEvent(UIEvent::AccountStatusChange(
|
|
|
|
self.hash, None,
|
|
|
|
)));
|
|
|
|
return Err(err);
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
2020-02-26 10:54:10 +02:00
|
|
|
let mut tree: Vec<MailboxNode> = Vec::new();
|
|
|
|
for (h, f) in ref_mailboxes.iter() {
|
2020-02-08 23:45:49 +02:00
|
|
|
if !f.is_subscribed() {
|
2020-02-26 10:54:10 +02:00
|
|
|
/* Skip unsubscribed mailbox */
|
2019-08-23 21:32:32 +03:00
|
|
|
continue;
|
|
|
|
}
|
2020-02-26 10:54:10 +02:00
|
|
|
mailbox_entries.entry(*h).and_modify(|entry| {
|
2020-07-24 22:07:43 +03:00
|
|
|
if entry.conf.mailbox_conf.autoload
|
2020-10-20 23:18:27 +03:00
|
|
|
|| (entry.ref_mailbox.special_usage() == SpecialUsageMailbox::Inbox
|
|
|
|
|| entry.ref_mailbox.special_usage() == SpecialUsageMailbox::Sent)
|
2020-07-24 22:07:43 +03:00
|
|
|
{
|
2020-08-07 00:39:17 +03:00
|
|
|
let total = entry.ref_mailbox.count().ok().unwrap_or((0, 0)).1;
|
|
|
|
entry.status = MailboxStatus::Parsing(0, total);
|
2020-08-20 17:37:19 +03:00
|
|
|
if let Ok(mailbox_job) = self.backend.write().unwrap().fetch(*h) {
|
|
|
|
let mailbox_job = mailbox_job.into_future();
|
2020-10-09 19:34:55 +03:00
|
|
|
let handle = if self.backend_capabilities.is_async {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
2023-07-14 00:23:24 +03:00
|
|
|
.spawn_specialized("fetch-mailbox".into(), mailbox_job)
|
2020-08-20 17:37:19 +03:00
|
|
|
} else {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
2023-07-14 00:23:24 +03:00
|
|
|
.spawn_blocking("fetch-mailbox".into(), mailbox_job)
|
2020-08-20 17:37:19 +03:00
|
|
|
};
|
2020-10-09 19:34:55 +03:00
|
|
|
let job_id = handle.job_id;
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
2020-08-20 17:37:19 +03:00
|
|
|
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
|
|
|
StatusEvent::NewJob(job_id),
|
2023-06-14 12:24:20 +03:00
|
|
|
)));
|
2020-10-09 19:34:55 +03:00
|
|
|
self.active_jobs.insert(
|
|
|
|
job_id,
|
|
|
|
JobRequest::Fetch {
|
|
|
|
mailbox_hash: *h,
|
|
|
|
handle,
|
|
|
|
},
|
|
|
|
);
|
2020-08-20 17:37:19 +03:00
|
|
|
self.active_job_instants
|
|
|
|
.insert(std::time::Instant::now(), job_id);
|
2020-06-27 21:40:46 +03:00
|
|
|
}
|
2020-03-02 12:06:19 +02:00
|
|
|
}
|
2020-02-10 02:11:07 +02:00
|
|
|
});
|
2020-03-12 09:47:39 +02:00
|
|
|
self.collection.new_mailbox(*h);
|
2017-09-16 15:05:28 +03:00
|
|
|
}
|
2019-08-23 21:32:32 +03:00
|
|
|
|
2020-02-26 10:54:10 +02:00
|
|
|
build_mailboxes_order(&mut tree, &mailbox_entries, &mut mailboxes_order);
|
|
|
|
self.mailboxes_order = mailboxes_order;
|
|
|
|
self.mailbox_entries = mailbox_entries;
|
2019-10-24 20:30:17 +03:00
|
|
|
self.tree = tree;
|
2020-06-22 17:32:51 +03:00
|
|
|
Ok(())
|
2017-09-14 18:08:14 +03:00
|
|
|
}
|
2019-10-24 20:30:17 +03:00
|
|
|
|
2020-02-26 10:54:10 +02:00
|
|
|
pub fn reload(&mut self, event: RefreshEvent, mailbox_hash: MailboxHash) -> Option<UIEvent> {
|
2020-09-11 16:58:56 +03:00
|
|
|
if !self.mailbox_entries[&mailbox_hash].status.is_available()
|
|
|
|
&& !self.mailbox_entries[&mailbox_hash].status.is_parsing()
|
|
|
|
{
|
2020-02-26 10:54:10 +02:00
|
|
|
self.event_queue.push_back((mailbox_hash, event));
|
2019-05-26 02:41:29 +03:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2018-09-23 19:55:29 +03:00
|
|
|
{
|
2023-04-30 19:39:41 +03:00
|
|
|
//let mailbox: &mut Mailbox =
|
|
|
|
// self.mailboxes[idx].as_mut().unwrap().as_mut().unwrap();
|
2020-08-26 19:13:18 +03:00
|
|
|
match event.kind {
|
2018-09-23 19:55:29 +03:00
|
|
|
RefreshEventKind::Update(old_hash, envelope) => {
|
2020-08-26 19:13:18 +03:00
|
|
|
if !self.collection.contains_key(&old_hash) {
|
|
|
|
return self.reload(
|
|
|
|
RefreshEvent {
|
|
|
|
account_hash: event.account_hash,
|
|
|
|
mailbox_hash: event.mailbox_hash,
|
|
|
|
kind: RefreshEventKind::Create(envelope),
|
|
|
|
},
|
|
|
|
mailbox_hash,
|
|
|
|
);
|
|
|
|
}
|
2019-11-12 13:09:43 +02:00
|
|
|
#[cfg(feature = "sqlite3")]
|
2023-11-24 12:58:21 +02:00
|
|
|
self.update_cached_env(*envelope.clone(), Some(old_hash));
|
2020-02-26 10:54:10 +02:00
|
|
|
self.collection.update(old_hash, *envelope, mailbox_hash);
|
2019-04-01 23:53:06 +03:00
|
|
|
return Some(EnvelopeUpdate(old_hash));
|
2018-10-14 19:49:16 +03:00
|
|
|
}
|
2020-02-28 09:09:43 +02:00
|
|
|
RefreshEventKind::NewFlags(env_hash, (flags, tags)) => {
|
2020-08-26 19:13:18 +03:00
|
|
|
if !self.collection.contains_key(&env_hash) {
|
|
|
|
return None;
|
|
|
|
}
|
2020-08-26 00:00:38 +03:00
|
|
|
self.collection
|
|
|
|
.envelopes
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.entry(env_hash)
|
|
|
|
.and_modify(|entry| {
|
2022-12-08 21:34:32 +02:00
|
|
|
entry.tags_mut().clear();
|
|
|
|
entry.tags_mut().extend(
|
|
|
|
tags.into_iter().map(|h| TagHash::from_bytes(h.as_bytes())),
|
|
|
|
);
|
2020-08-26 00:00:38 +03:00
|
|
|
entry.set_flags(flags);
|
|
|
|
});
|
2020-02-28 09:09:43 +02:00
|
|
|
#[cfg(feature = "sqlite3")]
|
2023-11-24 12:58:21 +02:00
|
|
|
if let Some(env) = {
|
|
|
|
let temp = self
|
|
|
|
.collection
|
|
|
|
.envelopes
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.get(&env_hash)
|
|
|
|
.cloned();
|
|
|
|
|
|
|
|
temp
|
|
|
|
} {
|
|
|
|
self.update_cached_env(env, None);
|
2020-02-28 09:09:43 +02:00
|
|
|
}
|
2020-06-23 12:27:10 +03:00
|
|
|
self.collection.update_flags(env_hash, mailbox_hash);
|
2020-02-28 09:09:43 +02:00
|
|
|
return Some(EnvelopeUpdate(env_hash));
|
|
|
|
}
|
2018-10-14 19:49:16 +03:00
|
|
|
RefreshEventKind::Rename(old_hash, new_hash) => {
|
2023-06-16 20:20:12 +03:00
|
|
|
log::trace!("rename {} to {}", old_hash, new_hash);
|
2020-06-09 15:38:13 +03:00
|
|
|
if !self.collection.rename(old_hash, new_hash, mailbox_hash) {
|
|
|
|
return Some(EnvelopeRename(old_hash, new_hash));
|
|
|
|
}
|
2019-11-12 13:09:43 +02:00
|
|
|
#[cfg(feature = "sqlite3")]
|
2023-11-24 12:58:21 +02:00
|
|
|
if let Some(env) = {
|
|
|
|
let temp = self
|
|
|
|
.collection
|
|
|
|
.envelopes
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.get(&new_hash)
|
|
|
|
.cloned();
|
|
|
|
|
|
|
|
temp
|
|
|
|
} {
|
|
|
|
self.update_cached_env(env, Some(old_hash));
|
2019-11-12 13:09:43 +02:00
|
|
|
}
|
2019-06-06 00:27:40 +03:00
|
|
|
return Some(EnvelopeRename(old_hash, new_hash));
|
2018-09-23 19:55:29 +03:00
|
|
|
}
|
|
|
|
RefreshEventKind::Create(envelope) => {
|
2019-05-26 02:34:03 +03:00
|
|
|
let env_hash = envelope.hash();
|
2019-11-02 12:14:31 +02:00
|
|
|
if self.collection.contains_key(&env_hash)
|
2020-07-21 07:53:38 +03:00
|
|
|
&& self
|
|
|
|
.collection
|
|
|
|
.get_mailbox(mailbox_hash)
|
|
|
|
.contains(&env_hash)
|
2019-09-11 17:57:55 +03:00
|
|
|
{
|
|
|
|
return None;
|
|
|
|
}
|
2019-11-02 12:14:31 +02:00
|
|
|
let (is_seen, is_draft) =
|
|
|
|
{ (envelope.is_seen(), envelope.flags().contains(Flag::DRAFT)) };
|
|
|
|
let (subject, from) = {
|
|
|
|
(
|
|
|
|
envelope.subject().into_owned(),
|
|
|
|
envelope.field_from_to_string(),
|
|
|
|
)
|
|
|
|
};
|
2019-11-05 16:42:55 +02:00
|
|
|
#[cfg(feature = "sqlite3")]
|
2020-09-18 18:39:27 +03:00
|
|
|
if self.settings.conf.search_backend == crate::conf::SearchBackend::Sqlite3 {
|
2024-02-23 14:48:17 +02:00
|
|
|
let handle = self.main_loop_handler.job_executor.spawn_specialized(
|
2023-07-14 00:23:24 +03:00
|
|
|
"sqlite3::insert".into(),
|
2024-02-23 14:48:17 +02:00
|
|
|
crate::sqlite3::AccountCache::insert(
|
2023-06-14 12:24:20 +03:00
|
|
|
(*envelope).clone(),
|
|
|
|
self.backend.clone(),
|
|
|
|
self.name.clone(),
|
|
|
|
),
|
|
|
|
);
|
2020-08-26 00:00:38 +03:00
|
|
|
self.insert_job(
|
2020-10-09 19:34:55 +03:00
|
|
|
handle.job_id,
|
2020-08-26 00:00:38 +03:00
|
|
|
JobRequest::Generic {
|
|
|
|
name: format!(
|
|
|
|
"Update envelope {} in sqlite3 cache",
|
|
|
|
envelope.message_id_display()
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
handle,
|
2023-05-01 16:22:35 +03:00
|
|
|
log_level: LogLevel::TRACE,
|
2020-08-26 00:00:38 +03:00
|
|
|
on_finish: None,
|
|
|
|
},
|
|
|
|
);
|
2019-11-05 16:42:55 +02:00
|
|
|
}
|
2020-03-12 09:47:39 +02:00
|
|
|
|
|
|
|
if self.collection.insert(*envelope, mailbox_hash) {
|
|
|
|
/* is a duplicate */
|
|
|
|
return None;
|
|
|
|
}
|
2019-05-26 02:34:03 +03:00
|
|
|
|
2020-02-26 10:54:10 +02:00
|
|
|
if self.mailbox_entries[&mailbox_hash]
|
2020-02-10 02:11:07 +02:00
|
|
|
.conf
|
2020-02-26 10:54:10 +02:00
|
|
|
.mailbox_conf
|
2020-02-10 02:11:07 +02:00
|
|
|
.ignore
|
|
|
|
.is_true()
|
|
|
|
{
|
2020-08-17 15:31:30 +03:00
|
|
|
return Some(UIEvent::MailboxUpdate((self.hash, mailbox_hash)));
|
2019-05-13 21:08:18 +03:00
|
|
|
}
|
2019-11-02 12:14:31 +02:00
|
|
|
|
2020-09-20 13:37:59 +03:00
|
|
|
let thread_hash = self.collection.get_env(env_hash).thread();
|
2020-07-21 07:53:38 +03:00
|
|
|
if self
|
|
|
|
.collection
|
|
|
|
.get_threads(mailbox_hash)
|
2020-09-20 13:37:59 +03:00
|
|
|
.thread_nodes()
|
|
|
|
.contains_key(&thread_hash)
|
2020-01-18 02:46:59 +02:00
|
|
|
{
|
2020-09-20 13:37:59 +03:00
|
|
|
let thread = self.collection.get_threads(mailbox_hash).find_group(
|
|
|
|
self.collection.get_threads(mailbox_hash)[&thread_hash].group,
|
|
|
|
);
|
|
|
|
if self
|
|
|
|
.collection
|
|
|
|
.get_threads(mailbox_hash)
|
|
|
|
.thread_ref(thread)
|
|
|
|
.snoozed()
|
|
|
|
{
|
|
|
|
return Some(UIEvent::MailboxUpdate((self.hash, mailbox_hash)));
|
|
|
|
}
|
2019-05-15 20:58:59 +03:00
|
|
|
}
|
2019-11-02 12:14:31 +02:00
|
|
|
if is_seen || is_draft {
|
2020-08-17 15:31:30 +03:00
|
|
|
return Some(UIEvent::MailboxUpdate((self.hash, mailbox_hash)));
|
2019-09-27 13:29:42 +03:00
|
|
|
}
|
|
|
|
|
2023-11-26 20:36:02 +02:00
|
|
|
return Some(Notification {
|
|
|
|
title: Some(format!("new e-mail from: {}", from).into()),
|
|
|
|
body: format!(
|
2019-09-27 13:29:42 +03:00
|
|
|
"{}\n{} {}",
|
2019-11-02 12:14:31 +02:00
|
|
|
subject,
|
2019-03-02 21:14:03 +02:00
|
|
|
self.name,
|
2020-02-26 10:54:10 +02:00
|
|
|
self.mailbox_entries[&mailbox_hash].name()
|
2023-11-26 20:36:02 +02:00
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
source: None,
|
|
|
|
kind: Some(crate::types::NotificationType::NewMail),
|
|
|
|
});
|
2018-09-23 19:55:29 +03:00
|
|
|
}
|
2020-06-23 17:23:42 +03:00
|
|
|
RefreshEventKind::Remove(env_hash) => {
|
2020-08-26 19:13:18 +03:00
|
|
|
if !self.collection.contains_key(&env_hash) {
|
|
|
|
return None;
|
|
|
|
}
|
2019-11-12 13:09:43 +02:00
|
|
|
#[cfg(feature = "sqlite3")]
|
2020-09-18 18:39:27 +03:00
|
|
|
if self.settings.conf.search_backend == crate::conf::SearchBackend::Sqlite3 {
|
2024-02-23 14:48:17 +02:00
|
|
|
let fut = crate::sqlite3::AccountCache::remove(self.name.clone(), env_hash);
|
|
|
|
let handle = self
|
|
|
|
.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.spawn_specialized("remove envelope from cache".into(), fut);
|
|
|
|
self.insert_job(
|
|
|
|
handle.job_id,
|
|
|
|
JobRequest::Refresh {
|
|
|
|
mailbox_hash,
|
|
|
|
handle,
|
|
|
|
},
|
|
|
|
);
|
2019-11-12 13:09:43 +02:00
|
|
|
}
|
2020-09-20 13:37:59 +03:00
|
|
|
let thread_hash = self.collection.get_env(env_hash).thread();
|
|
|
|
if !self
|
|
|
|
.collection
|
|
|
|
.get_threads(mailbox_hash)
|
|
|
|
.thread_nodes()
|
|
|
|
.contains_key(&thread_hash)
|
|
|
|
{
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
let thread_hash = self
|
|
|
|
.collection
|
|
|
|
.get_threads(mailbox_hash)
|
|
|
|
.find_group(self.collection.get_threads(mailbox_hash)[&thread_hash].group);
|
2020-06-23 17:23:42 +03:00
|
|
|
self.collection.remove(env_hash, mailbox_hash);
|
|
|
|
return Some(EnvelopeRemove(env_hash, thread_hash));
|
2018-09-23 19:55:29 +03:00
|
|
|
}
|
|
|
|
RefreshEventKind::Rescan => {
|
2020-08-20 17:37:19 +03:00
|
|
|
self.watch();
|
2018-09-23 19:55:29 +03:00
|
|
|
}
|
2020-07-05 17:22:06 +03:00
|
|
|
RefreshEventKind::Failure(err) => {
|
2023-06-16 20:20:12 +03:00
|
|
|
log::trace!("RefreshEvent Failure: {}", err.to_string());
|
2020-09-13 00:03:12 +03:00
|
|
|
while let Some((job_id, _)) =
|
|
|
|
self.active_jobs.iter().find(|(_, j)| j.is_watch())
|
|
|
|
{
|
|
|
|
let job_id = *job_id;
|
|
|
|
let j = self.active_jobs.remove(&job_id);
|
|
|
|
drop(j);
|
|
|
|
}
|
2019-12-12 01:01:11 +02:00
|
|
|
self.watch();
|
2023-11-26 20:36:02 +02:00
|
|
|
return Some(Notification {
|
|
|
|
title: Some("Account watch failed".into()),
|
|
|
|
body: err.to_string().into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Error(err.kind)),
|
|
|
|
source: Some(err),
|
|
|
|
});
|
2019-08-14 22:59:46 +03:00
|
|
|
}
|
2021-01-07 15:48:13 +02:00
|
|
|
RefreshEventKind::MailboxCreate(_new_mailbox) => {}
|
|
|
|
RefreshEventKind::MailboxDelete(_mailbox_hash) => {}
|
|
|
|
RefreshEventKind::MailboxRename {
|
|
|
|
old_mailbox_hash: _,
|
|
|
|
new_mailbox: _,
|
|
|
|
} => {}
|
|
|
|
RefreshEventKind::MailboxSubscribe(_mailbox_hash) => {}
|
|
|
|
RefreshEventKind::MailboxUnsubscribe(_mailbox_hash) => {}
|
2018-09-05 16:08:11 +03:00
|
|
|
}
|
|
|
|
}
|
2018-09-12 15:10:19 +03:00
|
|
|
None
|
2018-08-11 19:19:30 +03:00
|
|
|
}
|
2024-03-16 18:10:37 +02:00
|
|
|
|
2020-02-26 10:54:10 +02:00
|
|
|
pub fn refresh(&mut self, mailbox_hash: MailboxHash) -> Result<()> {
|
2020-01-08 21:41:57 +02:00
|
|
|
if let Some(ref refresh_command) = self.settings.conf().refresh_command {
|
2020-05-28 16:27:02 +03:00
|
|
|
let child = std::process::Command::new("sh")
|
2023-04-30 19:39:41 +03:00
|
|
|
.args(["-c", refresh_command])
|
2020-01-08 21:41:57 +02:00
|
|
|
.stdin(std::process::Stdio::null())
|
|
|
|
.stdout(std::process::Stdio::null())
|
|
|
|
.stderr(std::process::Stdio::piped())
|
|
|
|
.spawn()?;
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
2020-02-28 09:16:19 +02:00
|
|
|
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
|
|
|
StatusEvent::DisplayMessage(format!("Running command {}", refresh_command)),
|
2023-06-14 12:24:20 +03:00
|
|
|
)));
|
|
|
|
self.main_loop_handler
|
2024-03-23 10:43:58 +02:00
|
|
|
.send(ThreadEvent::UIEvent(UIEvent::Fork(ForkType::Generic {
|
|
|
|
id: refresh_command.to_string().into(),
|
|
|
|
command: Some(refresh_command.clone().into()),
|
|
|
|
child,
|
|
|
|
})));
|
2020-01-08 21:41:57 +02:00
|
|
|
return Ok(());
|
|
|
|
}
|
2020-09-10 21:01:40 +03:00
|
|
|
let refresh_job = self.backend.write().unwrap().refresh(mailbox_hash);
|
|
|
|
if let Ok(refresh_job) = refresh_job {
|
2020-10-09 19:34:55 +03:00
|
|
|
let handle = if self.backend_capabilities.is_async {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
2023-07-14 00:23:24 +03:00
|
|
|
.spawn_specialized("refresh".into(), refresh_job)
|
2020-08-20 17:37:19 +03:00
|
|
|
} else {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
2023-07-14 00:23:24 +03:00
|
|
|
.spawn_blocking("refresh".into(), refresh_job)
|
2020-08-20 17:37:19 +03:00
|
|
|
};
|
2020-10-09 19:34:55 +03:00
|
|
|
self.insert_job(
|
|
|
|
handle.job_id,
|
|
|
|
JobRequest::Refresh {
|
|
|
|
mailbox_hash,
|
|
|
|
handle,
|
|
|
|
},
|
|
|
|
);
|
2020-06-29 00:18:24 +03:00
|
|
|
}
|
2019-12-15 08:52:22 +02:00
|
|
|
Ok(())
|
2019-12-14 18:55:46 +02:00
|
|
|
}
|
2020-08-20 17:37:19 +03:00
|
|
|
|
2020-07-05 13:22:48 +03:00
|
|
|
pub fn watch(&mut self) {
|
2024-03-16 18:10:37 +02:00
|
|
|
if self.settings.account().manual_refresh
|
|
|
|
|| matches!(self.is_online, IsOnline::Err { ref value, ..} if !IsOnline::is_recoverable(value))
|
|
|
|
{
|
2019-12-14 18:55:46 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-08-20 17:37:19 +03:00
|
|
|
if !self.active_jobs.values().any(|j| j.is_watch()) {
|
|
|
|
match self.backend.read().unwrap().watch() {
|
|
|
|
Ok(fut) => {
|
2020-10-09 19:34:55 +03:00
|
|
|
let handle = if self.backend_capabilities.is_async {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.spawn_specialized("watch".into(), fut)
|
2020-08-20 17:37:19 +03:00
|
|
|
} else {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.spawn_blocking("watch".into(), fut)
|
2020-08-20 17:37:19 +03:00
|
|
|
};
|
2020-08-25 21:12:28 +03:00
|
|
|
self.active_jobs
|
2020-10-09 19:34:55 +03:00
|
|
|
.insert(handle.job_id, JobRequest::Watch { handle });
|
2020-07-05 13:22:48 +03:00
|
|
|
}
|
2021-09-05 12:39:15 +03:00
|
|
|
Err(e)
|
|
|
|
if e.kind == ErrorKind::NotSupported || e.kind == ErrorKind::NotImplemented => {
|
|
|
|
}
|
2020-07-05 13:22:48 +03:00
|
|
|
Err(e) => {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
2020-07-05 13:22:48 +03:00
|
|
|
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
2021-09-05 12:38:06 +03:00
|
|
|
StatusEvent::DisplayMessage(format!(
|
|
|
|
"Account `{}` watch action returned error: {}",
|
|
|
|
&self.name, e
|
|
|
|
)),
|
2023-06-14 12:24:20 +03:00
|
|
|
)));
|
2020-07-05 13:22:48 +03:00
|
|
|
}
|
2019-09-11 17:57:55 +03:00
|
|
|
}
|
|
|
|
}
|
2017-09-28 18:06:35 +03:00
|
|
|
}
|
2019-07-28 18:52:45 +03:00
|
|
|
|
2018-07-11 18:58:57 +03:00
|
|
|
pub fn len(&self) -> usize {
|
2020-02-10 02:11:07 +02:00
|
|
|
self.tree.len()
|
2018-07-11 18:58:57 +03:00
|
|
|
}
|
2023-06-19 00:13:53 +03:00
|
|
|
|
2018-08-23 15:36:52 +03:00
|
|
|
pub fn is_empty(&self) -> bool {
|
2020-02-10 02:11:07 +02:00
|
|
|
self.tree.is_empty()
|
2020-02-08 23:42:31 +02:00
|
|
|
}
|
|
|
|
|
2020-02-26 10:54:10 +02:00
|
|
|
pub fn list_mailboxes(&self) -> Vec<MailboxNode> {
|
|
|
|
let mut ret = Vec::with_capacity(self.mailbox_entries.len());
|
|
|
|
fn rec(node: &MailboxNode, ret: &mut Vec<MailboxNode>) {
|
2020-02-08 23:42:31 +02:00
|
|
|
ret.push(node.clone());
|
|
|
|
for c in node.children.iter() {
|
|
|
|
rec(c, ret);
|
2018-08-19 14:54:32 +03:00
|
|
|
}
|
|
|
|
}
|
2020-02-08 23:42:31 +02:00
|
|
|
for node in &self.tree {
|
|
|
|
rec(node, &mut ret);
|
2019-11-23 17:56:38 +02:00
|
|
|
}
|
2020-02-08 23:42:31 +02:00
|
|
|
ret
|
2019-04-26 11:04:30 +03:00
|
|
|
}
|
2020-02-08 23:42:31 +02:00
|
|
|
|
2024-03-16 15:05:15 +02:00
|
|
|
pub fn mailboxes_order(&self) -> &[MailboxHash] {
|
2020-02-26 10:54:10 +02:00
|
|
|
&self.mailboxes_order
|
2018-07-11 18:58:57 +03:00
|
|
|
}
|
2024-03-16 15:05:15 +02:00
|
|
|
|
2018-07-20 12:44:04 +03:00
|
|
|
pub fn name(&self) -> &str {
|
2018-07-11 23:12:25 +03:00
|
|
|
&self.name
|
|
|
|
}
|
2018-08-23 15:36:52 +03:00
|
|
|
|
2020-07-24 22:08:09 +03:00
|
|
|
pub fn hash(&self) -> AccountHash {
|
|
|
|
self.hash
|
|
|
|
}
|
|
|
|
|
2020-03-02 12:06:19 +02:00
|
|
|
pub fn load(&mut self, mailbox_hash: MailboxHash) -> result::Result<(), usize> {
|
2022-12-08 21:34:32 +02:00
|
|
|
if mailbox_hash.is_null() {
|
2020-02-08 23:42:31 +02:00
|
|
|
return Err(0);
|
|
|
|
}
|
2020-08-20 17:37:19 +03:00
|
|
|
match self.mailbox_entries[&mailbox_hash].status {
|
|
|
|
MailboxStatus::Available | MailboxStatus::Parsing(_, _)
|
|
|
|
if self
|
|
|
|
.collection
|
|
|
|
.mailboxes
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.contains_key(&mailbox_hash) =>
|
2020-02-10 02:11:07 +02:00
|
|
|
{
|
2020-08-20 17:37:19 +03:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
MailboxStatus::None => {
|
|
|
|
if !self.active_jobs.values().any(|j| j.is_fetch(mailbox_hash)) {
|
|
|
|
let mailbox_job = self.backend.write().unwrap().fetch(mailbox_hash);
|
|
|
|
match mailbox_job {
|
|
|
|
Ok(mailbox_job) => {
|
|
|
|
let mailbox_job = mailbox_job.into_future();
|
2020-10-09 19:34:55 +03:00
|
|
|
let handle = if self.backend_capabilities.is_async {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
2023-07-14 00:23:24 +03:00
|
|
|
.spawn_specialized("mailbox_fetch".into(), mailbox_job)
|
2020-08-20 17:37:19 +03:00
|
|
|
} else {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
2023-07-14 00:23:24 +03:00
|
|
|
.spawn_blocking("mailbox_fetch".into(), mailbox_job)
|
2020-08-20 17:37:19 +03:00
|
|
|
};
|
2020-10-09 19:34:55 +03:00
|
|
|
self.insert_job(
|
|
|
|
handle.job_id,
|
|
|
|
JobRequest::Fetch {
|
|
|
|
mailbox_hash,
|
|
|
|
handle,
|
|
|
|
},
|
|
|
|
);
|
2020-03-02 12:06:19 +02:00
|
|
|
}
|
2020-08-20 17:37:19 +03:00
|
|
|
Err(err) => {
|
2020-03-02 12:06:19 +02:00
|
|
|
self.mailbox_entries
|
|
|
|
.entry(mailbox_hash)
|
|
|
|
.and_modify(|entry| {
|
2020-07-13 18:49:27 +03:00
|
|
|
entry.status = MailboxStatus::Failed(err);
|
2020-03-02 12:06:19 +02:00
|
|
|
});
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(mailbox_hash)));
|
2020-03-02 12:06:19 +02:00
|
|
|
}
|
2019-12-14 19:56:43 +02:00
|
|
|
}
|
2020-08-20 17:37:19 +03:00
|
|
|
}
|
|
|
|
self.collection.new_mailbox(mailbox_hash);
|
|
|
|
Err(0)
|
|
|
|
}
|
|
|
|
_ => Err(0),
|
2019-08-14 23:01:57 +03:00
|
|
|
}
|
2018-08-03 13:46:08 +03:00
|
|
|
}
|
2019-02-16 16:37:14 +02:00
|
|
|
|
2019-12-03 13:27:35 +02:00
|
|
|
pub fn save_special(
|
2020-06-30 11:40:26 +03:00
|
|
|
&mut self,
|
2019-12-03 13:27:35 +02:00
|
|
|
bytes: &[u8],
|
2020-02-26 10:54:10 +02:00
|
|
|
mailbox_type: SpecialUsageMailbox,
|
2019-12-03 13:27:35 +02:00
|
|
|
flags: Flag,
|
2020-06-23 19:25:01 +03:00
|
|
|
) -> Result<MailboxHash> {
|
|
|
|
let mut saved_at: Option<MailboxHash> = None;
|
2020-02-26 10:54:10 +02:00
|
|
|
for mailbox in &[
|
|
|
|
self.special_use_mailbox(mailbox_type),
|
|
|
|
self.special_use_mailbox(SpecialUsageMailbox::Inbox),
|
|
|
|
self.special_use_mailbox(SpecialUsageMailbox::Normal),
|
2019-12-03 13:27:35 +02:00
|
|
|
] {
|
2020-12-25 06:09:46 +02:00
|
|
|
if let Some(mailbox_hash) = mailbox {
|
2022-08-25 15:17:18 +03:00
|
|
|
if let Err(err) = self.save(bytes, *mailbox_hash, Some(flags)) {
|
2023-05-01 16:22:35 +03:00
|
|
|
log::error!("Could not save in '{}' mailbox: {}.", *mailbox_hash, err);
|
2020-12-25 06:09:46 +02:00
|
|
|
} else {
|
|
|
|
saved_at = Some(*mailbox_hash);
|
|
|
|
break;
|
|
|
|
}
|
2019-12-03 13:27:35 +02:00
|
|
|
} else {
|
2020-12-25 06:09:46 +02:00
|
|
|
continue;
|
2019-12-03 13:27:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-23 19:25:01 +03:00
|
|
|
if let Some(mailbox_hash) = saved_at {
|
|
|
|
Ok(mailbox_hash)
|
|
|
|
} else {
|
2023-11-26 19:28:11 +02:00
|
|
|
let file = crate::types::File::create_temp_file(bytes, None, None, Some("eml"), false)?;
|
|
|
|
log::trace!("message saved in {}", file.path().display());
|
2023-05-01 16:22:35 +03:00
|
|
|
log::info!(
|
|
|
|
"Message was stored in {} so that you can restore it manually.",
|
2023-11-26 19:28:11 +02:00
|
|
|
file.path().display()
|
2019-12-03 13:27:35 +02:00
|
|
|
);
|
2022-12-08 22:20:05 +02:00
|
|
|
Err(Error::new(format!(
|
2019-12-03 13:27:35 +02:00
|
|
|
"Message was stored in {} so that you can restore it manually.",
|
2023-11-26 19:28:11 +02:00
|
|
|
file.path().display()
|
2019-12-03 13:27:35 +02:00
|
|
|
))
|
2022-09-11 15:19:40 +03:00
|
|
|
.set_summary("Could not save in any mailbox"))
|
2019-12-03 13:27:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-30 11:40:26 +03:00
|
|
|
pub fn save(
|
|
|
|
&mut self,
|
|
|
|
bytes: &[u8],
|
|
|
|
mailbox_hash: MailboxHash,
|
|
|
|
flags: Option<Flag>,
|
|
|
|
) -> Result<()> {
|
2023-05-16 19:22:13 +03:00
|
|
|
if self.settings.account.read_only {
|
2022-12-08 22:20:05 +02:00
|
|
|
return Err(Error::new(format!(
|
2019-07-18 20:16:51 +03:00
|
|
|
"Account {} is read-only.",
|
|
|
|
self.name.as_str()
|
|
|
|
)));
|
|
|
|
}
|
2020-06-30 11:40:26 +03:00
|
|
|
let job = self
|
|
|
|
.backend
|
2020-06-08 22:08:10 +03:00
|
|
|
.write()
|
|
|
|
.unwrap()
|
2020-06-30 11:40:26 +03:00
|
|
|
.save(bytes.to_vec(), mailbox_hash, flags)?;
|
2020-09-16 13:17:26 +03:00
|
|
|
|
2020-10-09 19:34:55 +03:00
|
|
|
let handle = if self.backend_capabilities.is_async {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.spawn_specialized("save".into(), job)
|
2020-09-16 13:17:26 +03:00
|
|
|
} else {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.spawn_blocking("save".into(), job)
|
2020-09-16 13:17:26 +03:00
|
|
|
};
|
2020-09-10 21:01:40 +03:00
|
|
|
self.insert_job(
|
2020-10-09 19:34:55 +03:00
|
|
|
handle.job_id,
|
2020-08-26 20:00:25 +03:00
|
|
|
JobRequest::SaveMessage {
|
|
|
|
bytes: bytes.to_vec(),
|
|
|
|
mailbox_hash,
|
|
|
|
handle,
|
|
|
|
},
|
|
|
|
);
|
2020-06-30 11:40:26 +03:00
|
|
|
Ok(())
|
2019-04-10 18:57:09 +03:00
|
|
|
}
|
2019-05-26 02:34:03 +03:00
|
|
|
|
Add smtp client support for sending mail in UI
`mailer_command` was removed, and a new setting `send_mail` was added.
Its possible values are a string, consisting of a shell command to
execute, or settings to configure an smtp server connection. The
configuration I used for testing this is:
[composing]
send_mail = { hostname = "smtp.mail.tld", port = 587, auth = { type = "auto", username = "yoshi", password = { type = "command_eval", value = "gpg2 --no-tty -q -d ~/.passwords/msmtp/yoshi.gpg" } }, security = { type = "STARTTLS" } }
For local smtp server:
[composing]
send_mail = { hostname = "localhost", port = 25, auth = { type = "none" }, security = { type = "none" } }
2020-07-15 14:38:43 +03:00
|
|
|
pub fn send(
|
|
|
|
&mut self,
|
|
|
|
message: String,
|
|
|
|
send_mail: crate::conf::composing::SendMail,
|
2023-07-16 11:37:09 +03:00
|
|
|
#[allow(unused_variables)] complete_in_background: bool,
|
2020-10-09 19:34:55 +03:00
|
|
|
) -> Result<Option<JoinHandle<Result<()>>>> {
|
2023-04-30 19:39:41 +03:00
|
|
|
use std::{
|
|
|
|
io::Write,
|
|
|
|
process::{Command, Stdio},
|
|
|
|
};
|
|
|
|
|
Add smtp client support for sending mail in UI
`mailer_command` was removed, and a new setting `send_mail` was added.
Its possible values are a string, consisting of a shell command to
execute, or settings to configure an smtp server connection. The
configuration I used for testing this is:
[composing]
send_mail = { hostname = "smtp.mail.tld", port = 587, auth = { type = "auto", username = "yoshi", password = { type = "command_eval", value = "gpg2 --no-tty -q -d ~/.passwords/msmtp/yoshi.gpg" } }, security = { type = "STARTTLS" } }
For local smtp server:
[composing]
send_mail = { hostname = "localhost", port = 25, auth = { type = "none" }, security = { type = "none" } }
2020-07-15 14:38:43 +03:00
|
|
|
use crate::conf::composing::SendMail;
|
|
|
|
match send_mail {
|
|
|
|
SendMail::ShellCommand(ref command) => {
|
|
|
|
if command.is_empty() {
|
2022-12-08 22:20:05 +02:00
|
|
|
return Err(Error::new(
|
Add smtp client support for sending mail in UI
`mailer_command` was removed, and a new setting `send_mail` was added.
Its possible values are a string, consisting of a shell command to
execute, or settings to configure an smtp server connection. The
configuration I used for testing this is:
[composing]
send_mail = { hostname = "smtp.mail.tld", port = 587, auth = { type = "auto", username = "yoshi", password = { type = "command_eval", value = "gpg2 --no-tty -q -d ~/.passwords/msmtp/yoshi.gpg" } }, security = { type = "STARTTLS" } }
For local smtp server:
[composing]
send_mail = { hostname = "localhost", port = 25, auth = { type = "none" }, security = { type = "none" } }
2020-07-15 14:38:43 +03:00
|
|
|
"send_mail shell command configuration value is empty",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
let mut msmtp = Command::new("sh")
|
2023-04-30 19:39:41 +03:00
|
|
|
.args(["-c", command])
|
Add smtp client support for sending mail in UI
`mailer_command` was removed, and a new setting `send_mail` was added.
Its possible values are a string, consisting of a shell command to
execute, or settings to configure an smtp server connection. The
configuration I used for testing this is:
[composing]
send_mail = { hostname = "smtp.mail.tld", port = 587, auth = { type = "auto", username = "yoshi", password = { type = "command_eval", value = "gpg2 --no-tty -q -d ~/.passwords/msmtp/yoshi.gpg" } }, security = { type = "STARTTLS" } }
For local smtp server:
[composing]
send_mail = { hostname = "localhost", port = 25, auth = { type = "none" }, security = { type = "none" } }
2020-07-15 14:38:43 +03:00
|
|
|
.stdin(Stdio::piped())
|
|
|
|
.stdout(Stdio::piped())
|
|
|
|
.spawn()
|
|
|
|
.expect("Failed to start mailer command");
|
|
|
|
{
|
|
|
|
let stdin = msmtp.stdin.as_mut().expect("failed to open stdin");
|
|
|
|
stdin
|
|
|
|
.write_all(message.as_bytes())
|
|
|
|
.expect("Failed to write to stdin");
|
|
|
|
}
|
|
|
|
let output = msmtp.wait().expect("Failed to wait on mailer");
|
|
|
|
if output.success() {
|
2023-05-01 16:22:35 +03:00
|
|
|
log::trace!("Message sent.");
|
Add smtp client support for sending mail in UI
`mailer_command` was removed, and a new setting `send_mail` was added.
Its possible values are a string, consisting of a shell command to
execute, or settings to configure an smtp server connection. The
configuration I used for testing this is:
[composing]
send_mail = { hostname = "smtp.mail.tld", port = 587, auth = { type = "auto", username = "yoshi", password = { type = "command_eval", value = "gpg2 --no-tty -q -d ~/.passwords/msmtp/yoshi.gpg" } }, security = { type = "STARTTLS" } }
For local smtp server:
[composing]
send_mail = { hostname = "localhost", port = 25, auth = { type = "none" }, security = { type = "none" } }
2020-07-15 14:38:43 +03:00
|
|
|
} else {
|
|
|
|
let error_message = if let Some(exit_code) = output.code() {
|
|
|
|
format!(
|
|
|
|
"Could not send e-mail using `{}`: Process exited with {}",
|
|
|
|
command, exit_code
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
format!(
|
|
|
|
"Could not send e-mail using `{}`: Process was killed by signal",
|
|
|
|
command
|
|
|
|
)
|
|
|
|
};
|
2023-05-01 16:22:35 +03:00
|
|
|
log::error!("{}", error_message);
|
2022-12-08 22:20:05 +02:00
|
|
|
return Err(Error::new(error_message).set_summary("Message not sent."));
|
Add smtp client support for sending mail in UI
`mailer_command` was removed, and a new setting `send_mail` was added.
Its possible values are a string, consisting of a shell command to
execute, or settings to configure an smtp server connection. The
configuration I used for testing this is:
[composing]
send_mail = { hostname = "smtp.mail.tld", port = 587, auth = { type = "auto", username = "yoshi", password = { type = "command_eval", value = "gpg2 --no-tty -q -d ~/.passwords/msmtp/yoshi.gpg" } }, security = { type = "STARTTLS" } }
For local smtp server:
[composing]
send_mail = { hostname = "localhost", port = 25, auth = { type = "none" }, security = { type = "none" } }
2020-07-15 14:38:43 +03:00
|
|
|
}
|
|
|
|
Ok(None)
|
|
|
|
}
|
|
|
|
#[cfg(feature = "smtp")]
|
|
|
|
SendMail::Smtp(conf) => {
|
2023-07-14 00:23:24 +03:00
|
|
|
let handle = self.main_loop_handler.job_executor.spawn_specialized(
|
|
|
|
"smtp".into(),
|
|
|
|
async move {
|
2023-06-14 12:24:20 +03:00
|
|
|
let mut smtp_connection =
|
|
|
|
melib::smtp::SmtpConnection::new_connection(conf).await?;
|
|
|
|
smtp_connection.mail_transaction(&message, None).await
|
2023-07-14 00:23:24 +03:00
|
|
|
},
|
|
|
|
);
|
Add smtp client support for sending mail in UI
`mailer_command` was removed, and a new setting `send_mail` was added.
Its possible values are a string, consisting of a shell command to
execute, or settings to configure an smtp server connection. The
configuration I used for testing this is:
[composing]
send_mail = { hostname = "smtp.mail.tld", port = 587, auth = { type = "auto", username = "yoshi", password = { type = "command_eval", value = "gpg2 --no-tty -q -d ~/.passwords/msmtp/yoshi.gpg" } }, security = { type = "STARTTLS" } }
For local smtp server:
[composing]
send_mail = { hostname = "localhost", port = 25, auth = { type = "none" }, security = { type = "none" } }
2020-07-15 14:38:43 +03:00
|
|
|
if complete_in_background {
|
2020-10-09 19:34:55 +03:00
|
|
|
self.insert_job(handle.job_id, JobRequest::SendMessageBackground { handle });
|
Add smtp client support for sending mail in UI
`mailer_command` was removed, and a new setting `send_mail` was added.
Its possible values are a string, consisting of a shell command to
execute, or settings to configure an smtp server connection. The
configuration I used for testing this is:
[composing]
send_mail = { hostname = "smtp.mail.tld", port = 587, auth = { type = "auto", username = "yoshi", password = { type = "command_eval", value = "gpg2 --no-tty -q -d ~/.passwords/msmtp/yoshi.gpg" } }, security = { type = "STARTTLS" } }
For local smtp server:
[composing]
send_mail = { hostname = "localhost", port = 25, auth = { type = "none" }, security = { type = "none" } }
2020-07-15 14:38:43 +03:00
|
|
|
return Ok(None);
|
|
|
|
} else {
|
2020-10-09 19:34:55 +03:00
|
|
|
self.insert_job(handle.job_id, JobRequest::SendMessage);
|
Add smtp client support for sending mail in UI
`mailer_command` was removed, and a new setting `send_mail` was added.
Its possible values are a string, consisting of a shell command to
execute, or settings to configure an smtp server connection. The
configuration I used for testing this is:
[composing]
send_mail = { hostname = "smtp.mail.tld", port = 587, auth = { type = "auto", username = "yoshi", password = { type = "command_eval", value = "gpg2 --no-tty -q -d ~/.passwords/msmtp/yoshi.gpg" } }, security = { type = "STARTTLS" } }
For local smtp server:
[composing]
send_mail = { hostname = "localhost", port = 25, auth = { type = "none" }, security = { type = "none" } }
2020-07-15 14:38:43 +03:00
|
|
|
}
|
2020-10-09 19:34:55 +03:00
|
|
|
Ok(Some(handle))
|
Add smtp client support for sending mail in UI
`mailer_command` was removed, and a new setting `send_mail` was added.
Its possible values are a string, consisting of a shell command to
execute, or settings to configure an smtp server connection. The
configuration I used for testing this is:
[composing]
send_mail = { hostname = "smtp.mail.tld", port = 587, auth = { type = "auto", username = "yoshi", password = { type = "command_eval", value = "gpg2 --no-tty -q -d ~/.passwords/msmtp/yoshi.gpg" } }, security = { type = "STARTTLS" } }
For local smtp server:
[composing]
send_mail = { hostname = "localhost", port = 25, auth = { type = "none" }, security = { type = "none" } }
2020-07-15 14:38:43 +03:00
|
|
|
}
|
2021-09-04 00:32:57 +03:00
|
|
|
SendMail::ServerSubmission => {
|
|
|
|
if self.backend_capabilities.supports_submission {
|
2022-08-25 15:17:18 +03:00
|
|
|
let job =
|
|
|
|
self.backend
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.submit(message.into_bytes(), None, None)?;
|
2021-09-04 00:32:57 +03:00
|
|
|
|
|
|
|
let handle = if self.backend_capabilities.is_async {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.spawn_specialized("server_submission".into(), job)
|
2021-09-04 00:32:57 +03:00
|
|
|
} else {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.spawn_blocking("server_submission".into(), job)
|
2021-09-04 00:32:57 +03:00
|
|
|
};
|
|
|
|
self.insert_job(handle.job_id, JobRequest::SendMessageBackground { handle });
|
|
|
|
return Ok(None);
|
|
|
|
}
|
2022-12-08 22:20:05 +02:00
|
|
|
Err(Error::new("Server does not support submission.")
|
2022-08-25 15:17:18 +03:00
|
|
|
.set_summary("Message not sent."))
|
2021-09-04 00:32:57 +03:00
|
|
|
}
|
Add smtp client support for sending mail in UI
`mailer_command` was removed, and a new setting `send_mail` was added.
Its possible values are a string, consisting of a shell command to
execute, or settings to configure an smtp server connection. The
configuration I used for testing this is:
[composing]
send_mail = { hostname = "smtp.mail.tld", port = 587, auth = { type = "auto", username = "yoshi", password = { type = "command_eval", value = "gpg2 --no-tty -q -d ~/.passwords/msmtp/yoshi.gpg" } }, security = { type = "STARTTLS" } }
For local smtp server:
[composing]
send_mail = { hostname = "localhost", port = 25, auth = { type = "none" }, security = { type = "none" } }
2020-07-15 14:38:43 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-08 16:52:13 +03:00
|
|
|
pub fn send_async(
|
|
|
|
&self,
|
|
|
|
send_mail: crate::conf::composing::SendMail,
|
|
|
|
) -> impl FnOnce(Arc<String>) -> Pin<Box<dyn Future<Output = Result<()>> + Send>> + Send {
|
2021-09-04 00:32:57 +03:00
|
|
|
let capabilities = self.backend_capabilities.clone();
|
|
|
|
let backend = self.backend.clone();
|
2023-09-13 18:51:30 +03:00
|
|
|
move |message: Arc<String>| -> Pin<Box<dyn Future<Output = Result<()>> + Send>> {
|
2020-10-08 16:52:13 +03:00
|
|
|
Box::pin(async move {
|
2023-04-30 19:39:41 +03:00
|
|
|
use std::{
|
|
|
|
io::Write,
|
|
|
|
process::{Command, Stdio},
|
|
|
|
};
|
|
|
|
|
2020-10-08 16:52:13 +03:00
|
|
|
use crate::conf::composing::SendMail;
|
|
|
|
match send_mail {
|
|
|
|
SendMail::ShellCommand(ref command) => {
|
|
|
|
if command.is_empty() {
|
2022-12-08 22:20:05 +02:00
|
|
|
return Err(Error::new(
|
2020-10-08 16:52:13 +03:00
|
|
|
"send_mail shell command configuration value is empty",
|
|
|
|
));
|
|
|
|
}
|
|
|
|
let mut msmtp = Command::new("sh")
|
2023-04-30 19:39:41 +03:00
|
|
|
.args(["-c", command])
|
2020-10-08 16:52:13 +03:00
|
|
|
.stdin(Stdio::piped())
|
|
|
|
.stdout(Stdio::piped())
|
|
|
|
.spawn()
|
|
|
|
.expect("Failed to start mailer command");
|
|
|
|
{
|
|
|
|
let stdin = msmtp.stdin.as_mut().expect("failed to open stdin");
|
|
|
|
stdin
|
|
|
|
.write_all(message.as_bytes())
|
|
|
|
.expect("Failed to write to stdin");
|
|
|
|
}
|
|
|
|
let output = msmtp.wait().expect("Failed to wait on mailer");
|
|
|
|
if output.success() {
|
2023-05-01 16:22:35 +03:00
|
|
|
log::trace!("Message sent.");
|
2020-10-08 16:52:13 +03:00
|
|
|
} else {
|
|
|
|
let error_message = if let Some(exit_code) = output.code() {
|
|
|
|
format!(
|
|
|
|
"Could not send e-mail using `{}`: Process exited with {}",
|
|
|
|
command, exit_code
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
format!(
|
2023-04-30 19:39:41 +03:00
|
|
|
"Could not send e-mail using `{}`: Process was killed by \
|
|
|
|
signal",
|
|
|
|
command
|
|
|
|
)
|
2020-10-08 16:52:13 +03:00
|
|
|
};
|
2023-05-01 16:22:35 +03:00
|
|
|
log::error!("{}", error_message);
|
2022-12-27 18:40:26 +02:00
|
|
|
return Err(Error::new(error_message).set_summary("Message not sent."));
|
2020-10-08 16:52:13 +03:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
#[cfg(feature = "smtp")]
|
|
|
|
SendMail::Smtp(conf) => {
|
|
|
|
let mut smtp_connection =
|
|
|
|
melib::smtp::SmtpConnection::new_connection(conf).await?;
|
|
|
|
smtp_connection
|
|
|
|
.mail_transaction(message.as_str(), None)
|
|
|
|
.await
|
|
|
|
}
|
2021-09-04 00:32:57 +03:00
|
|
|
SendMail::ServerSubmission => {
|
|
|
|
if capabilities.supports_submission {
|
|
|
|
let fut = backend.write().unwrap().submit(
|
|
|
|
message.as_bytes().to_vec(),
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
)?;
|
|
|
|
fut.await?;
|
|
|
|
return Ok(());
|
|
|
|
}
|
2022-12-08 22:20:05 +02:00
|
|
|
Err(Error::new("Server does not support submission.")
|
2022-08-25 15:17:18 +03:00
|
|
|
.set_summary("Message not sent."))
|
2021-09-04 00:32:57 +03:00
|
|
|
}
|
2020-10-08 16:52:13 +03:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-18 21:13:58 +03:00
|
|
|
pub fn contains_key(&self, h: EnvelopeHash) -> bool {
|
|
|
|
self.collection.contains_key(&h)
|
2019-05-26 02:34:03 +03:00
|
|
|
}
|
2020-06-23 17:21:50 +03:00
|
|
|
pub fn operation(&self, h: EnvelopeHash) -> Result<Box<dyn BackendOp>> {
|
|
|
|
let operation = self.backend.read().unwrap().operation(h)?;
|
2023-05-16 19:22:13 +03:00
|
|
|
Ok(if self.settings.account.read_only {
|
2019-11-03 13:12:28 +02:00
|
|
|
ReadOnlyOp::new(operation)
|
|
|
|
} else {
|
|
|
|
operation
|
2020-06-23 17:21:50 +03:00
|
|
|
})
|
2019-05-26 02:34:03 +03:00
|
|
|
}
|
2019-11-02 12:14:31 +02:00
|
|
|
|
2020-02-26 10:54:10 +02:00
|
|
|
pub fn mailbox_operation(
|
2020-02-06 01:49:18 +02:00
|
|
|
&mut self,
|
2020-07-25 13:08:36 +03:00
|
|
|
op: crate::command::actions::MailboxOperation,
|
2020-08-25 22:56:45 +03:00
|
|
|
) -> Result<()> {
|
2020-07-25 13:08:36 +03:00
|
|
|
use crate::command::actions::MailboxOperation;
|
2023-05-16 19:22:13 +03:00
|
|
|
if self.settings.account.read_only {
|
2022-12-08 22:20:05 +02:00
|
|
|
return Err(Error::new("Account is read-only."));
|
2020-02-06 01:49:18 +02:00
|
|
|
}
|
2020-01-15 12:31:49 +02:00
|
|
|
match op {
|
2020-02-26 10:54:10 +02:00
|
|
|
MailboxOperation::Create(path) => {
|
2020-08-25 22:56:45 +03:00
|
|
|
let job = self
|
|
|
|
.backend
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.create_mailbox(path.to_string())?;
|
2020-10-09 19:34:55 +03:00
|
|
|
let handle = if self.backend_capabilities.is_async {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.spawn_specialized("create_mailbox".into(), job)
|
2020-02-26 10:54:10 +02:00
|
|
|
} else {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.spawn_blocking("create_mailbox".into(), job)
|
2020-02-26 10:54:10 +02:00
|
|
|
};
|
2020-10-09 19:34:55 +03:00
|
|
|
self.insert_job(handle.job_id, JobRequest::CreateMailbox { path, handle });
|
2020-08-25 22:56:45 +03:00
|
|
|
Ok(())
|
2020-02-06 01:49:18 +02:00
|
|
|
}
|
2020-02-26 10:54:10 +02:00
|
|
|
MailboxOperation::Delete(path) => {
|
|
|
|
if self.mailbox_entries.len() == 1 {
|
2022-12-08 22:20:05 +02:00
|
|
|
return Err(Error::new("Cannot delete only mailbox."));
|
2020-01-15 12:31:49 +02:00
|
|
|
}
|
2020-08-25 22:56:45 +03:00
|
|
|
|
2020-06-08 22:08:10 +03:00
|
|
|
let mailbox_hash = self.mailbox_by_path(&path)?;
|
2020-08-25 22:56:45 +03:00
|
|
|
let job = self.backend.write().unwrap().delete_mailbox(mailbox_hash)?;
|
2020-10-09 19:34:55 +03:00
|
|
|
let handle = if self.backend_capabilities.is_async {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.spawn_specialized("delete_mailbox".into(), job)
|
2020-08-25 22:56:45 +03:00
|
|
|
} else {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.spawn_blocking("delete_mailbox".into(), job)
|
2020-08-25 22:56:45 +03:00
|
|
|
};
|
|
|
|
self.insert_job(
|
2020-10-09 19:34:55 +03:00
|
|
|
handle.job_id,
|
2020-08-25 22:56:45 +03:00
|
|
|
JobRequest::DeleteMailbox {
|
2020-02-26 10:54:10 +02:00
|
|
|
mailbox_hash,
|
2020-08-25 22:56:45 +03:00
|
|
|
handle,
|
|
|
|
},
|
2020-02-22 10:57:59 +02:00
|
|
|
);
|
2020-08-25 22:56:45 +03:00
|
|
|
Ok(())
|
2020-01-15 12:31:49 +02:00
|
|
|
}
|
2020-03-24 21:01:06 +02:00
|
|
|
MailboxOperation::Subscribe(path) => {
|
2020-06-08 22:08:10 +03:00
|
|
|
let mailbox_hash = self.mailbox_by_path(&path)?;
|
2022-11-28 15:12:54 +02:00
|
|
|
let job = self
|
|
|
|
.backend
|
2020-03-24 21:01:06 +02:00
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.set_mailbox_subscription(mailbox_hash, true)?;
|
2022-11-28 15:12:54 +02:00
|
|
|
let handle = if self.backend_capabilities.is_async {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.spawn_specialized("subscribe_mailbox".into(), job)
|
2022-11-28 15:12:54 +02:00
|
|
|
} else {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.spawn_blocking("subscribe_mailbox".into(), job)
|
2022-11-28 15:12:54 +02:00
|
|
|
};
|
|
|
|
self.insert_job(
|
|
|
|
handle.job_id,
|
|
|
|
JobRequest::SetMailboxSubscription {
|
|
|
|
mailbox_hash,
|
|
|
|
new_value: true,
|
|
|
|
handle,
|
|
|
|
},
|
|
|
|
);
|
2020-08-25 22:56:45 +03:00
|
|
|
Ok(())
|
2020-03-24 21:01:06 +02:00
|
|
|
}
|
|
|
|
MailboxOperation::Unsubscribe(path) => {
|
2020-06-08 22:08:10 +03:00
|
|
|
let mailbox_hash = self.mailbox_by_path(&path)?;
|
2022-11-28 15:12:54 +02:00
|
|
|
let job = self
|
|
|
|
.backend
|
2020-03-24 21:01:06 +02:00
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.set_mailbox_subscription(mailbox_hash, false)?;
|
2022-11-28 15:12:54 +02:00
|
|
|
let handle = if self.backend_capabilities.is_async {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.spawn_specialized("unsubscribe_mailbox".into(), job)
|
2022-11-28 15:12:54 +02:00
|
|
|
} else {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.spawn_blocking("unsubscribe_mailbox".into(), job)
|
2022-11-28 15:12:54 +02:00
|
|
|
};
|
|
|
|
self.insert_job(
|
|
|
|
handle.job_id,
|
|
|
|
JobRequest::SetMailboxSubscription {
|
|
|
|
mailbox_hash,
|
|
|
|
new_value: false,
|
|
|
|
handle,
|
|
|
|
},
|
|
|
|
);
|
2020-08-25 22:56:45 +03:00
|
|
|
Ok(())
|
2020-03-24 21:01:06 +02:00
|
|
|
}
|
2022-12-08 22:20:05 +02:00
|
|
|
MailboxOperation::Rename(_, _) => Err(Error::new("Not implemented.")),
|
|
|
|
MailboxOperation::SetPermissions(_) => Err(Error::new("Not implemented.")),
|
2020-01-15 12:31:49 +02:00
|
|
|
}
|
2019-08-26 19:44:05 +03:00
|
|
|
}
|
2019-09-09 21:38:37 +03:00
|
|
|
|
2020-06-08 22:08:10 +03:00
|
|
|
pub fn special_use_mailbox(&self, special_use: SpecialUsageMailbox) -> Option<MailboxHash> {
|
2019-09-23 09:30:23 +03:00
|
|
|
let ret = self
|
2020-02-26 10:54:10 +02:00
|
|
|
.mailbox_entries
|
2019-09-23 09:30:23 +03:00
|
|
|
.iter()
|
2020-02-26 10:54:10 +02:00
|
|
|
.find(|(_, f)| f.conf.mailbox_conf().usage == Some(special_use));
|
2022-08-25 15:17:18 +03:00
|
|
|
ret.as_ref().map(|ret| ret.1.ref_mailbox.hash())
|
2019-09-23 09:30:23 +03:00
|
|
|
}
|
2019-10-24 20:30:17 +03:00
|
|
|
|
2023-04-30 19:39:41 +03:00
|
|
|
/* Call only in Context::is_online, since only Context can launch the watcher
|
|
|
|
* threads if an account goes from offline to online. */
|
2019-12-14 18:46:12 +02:00
|
|
|
pub fn is_online(&mut self) -> Result<()> {
|
2023-06-19 00:13:53 +03:00
|
|
|
let (ret, wait) = match &mut self.is_online {
|
|
|
|
IsOnline::Uninit => (Err(Error::new("Attempting connection.")), None),
|
|
|
|
IsOnline::True => return Ok(()),
|
|
|
|
IsOnline::Err {
|
|
|
|
value,
|
|
|
|
ref mut retries,
|
|
|
|
} => {
|
|
|
|
let ret = Err(value.clone());
|
2024-03-16 18:10:37 +02:00
|
|
|
if !IsOnline::is_recoverable(value) {
|
2023-06-19 00:13:53 +03:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
let wait = if value.kind.is_timeout()
|
|
|
|
|| value.kind.is_network_down()
|
|
|
|
|| value.kind.is_oserror()
|
|
|
|
{
|
|
|
|
let oldval = *retries;
|
|
|
|
if oldval != 8 {
|
|
|
|
*retries *= 2;
|
|
|
|
}
|
|
|
|
Some(Duration::from_millis(
|
2024-01-01 15:23:22 +02:00
|
|
|
oldval * (4 * u64::from(melib::utils::random::random_u8())),
|
2023-06-19 00:13:53 +03:00
|
|
|
))
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
};
|
2020-07-05 19:17:28 +03:00
|
|
|
|
2023-06-19 00:13:53 +03:00
|
|
|
(ret, wait)
|
|
|
|
}
|
|
|
|
};
|
2020-08-20 17:37:19 +03:00
|
|
|
if !self.active_jobs.values().any(JobRequest::is_online) {
|
2023-06-19 00:13:53 +03:00
|
|
|
let online_fut = self.backend.read().unwrap().is_online();
|
|
|
|
if let Ok(online_fut) = online_fut {
|
|
|
|
use melib::utils::futures::sleep;
|
|
|
|
|
|
|
|
let handle = match (wait, self.backend_capabilities.is_async) {
|
2023-07-14 00:23:24 +03:00
|
|
|
(Some(wait), true) => self.main_loop_handler.job_executor.spawn_specialized(
|
|
|
|
"is_online".into(),
|
|
|
|
async move {
|
|
|
|
sleep(wait).await;
|
|
|
|
online_fut.await
|
|
|
|
},
|
|
|
|
),
|
2023-06-19 00:13:53 +03:00
|
|
|
(None, true) => self
|
|
|
|
.main_loop_handler
|
2023-06-14 12:24:20 +03:00
|
|
|
.job_executor
|
2023-07-14 00:23:24 +03:00
|
|
|
.spawn_specialized("is_online".into(), online_fut),
|
|
|
|
(Some(wait), false) => self.main_loop_handler.job_executor.spawn_blocking(
|
|
|
|
"is_online".into(),
|
|
|
|
async move {
|
|
|
|
sleep(wait).await;
|
|
|
|
online_fut.await
|
|
|
|
},
|
|
|
|
),
|
2023-06-19 00:13:53 +03:00
|
|
|
(None, false) => self
|
|
|
|
.main_loop_handler
|
2023-06-14 12:24:20 +03:00
|
|
|
.job_executor
|
2023-07-14 00:23:24 +03:00
|
|
|
.spawn_blocking("is_online".into(), online_fut),
|
2020-08-20 17:37:19 +03:00
|
|
|
};
|
2020-10-09 19:34:55 +03:00
|
|
|
self.insert_job(handle.job_id, JobRequest::IsOnline { handle });
|
2020-06-27 21:40:46 +03:00
|
|
|
}
|
2020-02-28 09:16:19 +02:00
|
|
|
}
|
2023-06-19 00:13:53 +03:00
|
|
|
ret
|
2019-10-24 20:30:17 +03:00
|
|
|
}
|
2019-10-30 21:09:48 +02:00
|
|
|
|
|
|
|
pub fn search(
|
|
|
|
&self,
|
|
|
|
search_term: &str,
|
2020-07-05 13:22:48 +03:00
|
|
|
_sort: (SortField, SortOrder),
|
2020-02-26 10:54:10 +02:00
|
|
|
mailbox_hash: MailboxHash,
|
2020-06-30 11:40:26 +03:00
|
|
|
) -> ResultFuture<SmallVec<[EnvelopeHash; 512]>> {
|
2020-08-06 19:45:08 +03:00
|
|
|
let query = melib::search::Query::try_from(search_term)?;
|
2020-07-16 23:58:46 +03:00
|
|
|
match self.settings.conf.search_backend {
|
|
|
|
#[cfg(feature = "sqlite3")]
|
2024-02-23 14:48:17 +02:00
|
|
|
crate::conf::SearchBackend::Sqlite3 => Ok(Box::pin(
|
|
|
|
crate::sqlite3::AccountCache::search(self.name.clone(), query, _sort),
|
|
|
|
)),
|
2020-09-18 21:38:50 +03:00
|
|
|
crate::conf::SearchBackend::Auto | crate::conf::SearchBackend::None => {
|
2020-07-25 17:53:04 +03:00
|
|
|
if self.backend_capabilities.supports_search {
|
2020-07-16 23:58:46 +03:00
|
|
|
self.backend
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.search(query, Some(mailbox_hash))
|
2020-06-23 17:21:50 +03:00
|
|
|
} else {
|
2020-08-06 19:45:08 +03:00
|
|
|
use melib::search::QueryTrait;
|
2020-07-16 23:58:46 +03:00
|
|
|
let mut ret = SmallVec::new();
|
|
|
|
let envelopes = self.collection.envelopes.read().unwrap();
|
2020-07-21 07:53:38 +03:00
|
|
|
for &env_hash in self.collection.get_mailbox(mailbox_hash).iter() {
|
2020-07-25 18:06:42 +03:00
|
|
|
if let Some(envelope) = envelopes.get(&env_hash) {
|
|
|
|
if envelope.is_match(&query) {
|
|
|
|
ret.push(env_hash);
|
|
|
|
}
|
2020-07-16 23:58:46 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(Box::pin(async { Ok(ret) }))
|
2019-10-30 21:09:48 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-08 22:08:10 +03:00
|
|
|
|
2024-03-16 15:01:28 +02:00
|
|
|
pub fn default_mailbox(&self) -> Option<MailboxHash> {
|
|
|
|
self.settings
|
|
|
|
.default_mailbox
|
|
|
|
.or_else(|| Some(*self.mailboxes_order.first()?))
|
|
|
|
}
|
|
|
|
|
2020-06-08 22:08:10 +03:00
|
|
|
pub fn mailbox_by_path(&self, path: &str) -> Result<MailboxHash> {
|
|
|
|
if let Some((mailbox_hash, _)) = self
|
|
|
|
.mailbox_entries
|
|
|
|
.iter()
|
|
|
|
.find(|(_, f)| f.ref_mailbox.path() == path)
|
|
|
|
{
|
|
|
|
Ok(*mailbox_hash)
|
|
|
|
} else {
|
2022-12-08 22:20:05 +02:00
|
|
|
Err(Error::new("Mailbox with that path not found."))
|
2020-06-08 22:08:10 +03:00
|
|
|
}
|
|
|
|
}
|
2020-06-27 21:40:46 +03:00
|
|
|
|
|
|
|
pub fn process_event(&mut self, job_id: &JobId) -> bool {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
2020-09-10 21:01:40 +03:00
|
|
|
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
|
|
|
StatusEvent::JobFinished(*job_id),
|
2023-06-14 12:24:20 +03:00
|
|
|
)));
|
2020-09-10 21:01:40 +03:00
|
|
|
|
2020-12-25 06:09:46 +02:00
|
|
|
if let Some(mut job) = self.active_jobs.remove(job_id) {
|
2023-07-14 00:23:24 +03:00
|
|
|
let job_id = *job_id;
|
2020-12-25 06:09:46 +02:00
|
|
|
match job {
|
2020-10-09 19:34:55 +03:00
|
|
|
JobRequest::Mailboxes { ref mut handle } => {
|
2020-12-24 21:56:24 +02:00
|
|
|
if let Ok(Some(mailboxes)) = handle.chan.try_recv() {
|
2020-08-20 17:37:19 +03:00
|
|
|
if let Err(err) = mailboxes.and_then(|mailboxes| self.init(mailboxes)) {
|
2024-03-16 18:10:37 +02:00
|
|
|
if !IsOnline::is_recoverable(&err) {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
2023-11-26 20:36:02 +02:00
|
|
|
UIEvent::Notification {
|
2024-03-16 18:10:37 +02:00
|
|
|
title: Some(self.name.clone().into()),
|
|
|
|
source: Some(err.clone()),
|
2023-11-26 20:36:02 +02:00
|
|
|
body: err.to_string().into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Error(err.kind)),
|
|
|
|
},
|
2023-06-14 12:24:20 +03:00
|
|
|
));
|
2023-06-19 00:13:53 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
2024-03-16 18:10:37 +02:00
|
|
|
UIEvent::AccountStatusChange(
|
|
|
|
self.hash,
|
|
|
|
Some(err.to_string().into()),
|
|
|
|
),
|
2023-06-19 00:13:53 +03:00
|
|
|
));
|
2024-03-16 18:10:37 +02:00
|
|
|
self.is_online.set_err(err);
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.set_job_success(job_id, false);
|
2020-08-02 00:46:37 +03:00
|
|
|
return true;
|
|
|
|
}
|
2020-09-10 21:01:40 +03:00
|
|
|
let mailboxes_job = self.backend.read().unwrap().mailboxes();
|
|
|
|
if let Ok(mailboxes_job) = mailboxes_job {
|
2020-10-09 19:34:55 +03:00
|
|
|
let handle = if self.backend_capabilities.is_async {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
2023-07-14 00:23:24 +03:00
|
|
|
.spawn_specialized("mailboxes_list".into(), mailboxes_job)
|
2020-09-16 13:17:26 +03:00
|
|
|
} else {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
2023-07-14 00:23:24 +03:00
|
|
|
.spawn_blocking("mailboxes_list".into(), mailboxes_job)
|
2020-09-16 13:17:26 +03:00
|
|
|
};
|
2020-10-09 19:34:55 +03:00
|
|
|
self.insert_job(handle.job_id, JobRequest::Mailboxes { handle });
|
2020-09-10 21:01:40 +03:00
|
|
|
};
|
2022-10-04 15:42:24 +03:00
|
|
|
} else {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
|
|
|
UIEvent::AccountStatusChange(
|
2022-10-04 15:42:24 +03:00
|
|
|
self.hash,
|
|
|
|
Some("Loaded mailboxes.".into()),
|
2023-06-14 12:24:20 +03:00
|
|
|
),
|
|
|
|
));
|
2020-06-27 21:40:46 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-09 19:34:55 +03:00
|
|
|
JobRequest::Fetch {
|
|
|
|
mailbox_hash,
|
|
|
|
ref mut handle,
|
|
|
|
..
|
|
|
|
} => {
|
2023-06-16 20:20:12 +03:00
|
|
|
log::trace!("got payload in status for {}", mailbox_hash);
|
2020-12-24 21:56:24 +02:00
|
|
|
match handle.chan.try_recv() {
|
|
|
|
Err(_) => {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.set_job_success(job_id, false);
|
2020-12-24 21:56:24 +02:00
|
|
|
/* canceled */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Ok(None) => {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Ok(Some((None, _))) => {
|
2023-06-16 20:20:12 +03:00
|
|
|
log::trace!("finished in status for {}", mailbox_hash);
|
2020-12-24 21:56:24 +02:00
|
|
|
self.mailbox_entries
|
|
|
|
.entry(mailbox_hash)
|
|
|
|
.and_modify(|entry| {
|
|
|
|
entry.status = MailboxStatus::Available;
|
|
|
|
});
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
|
|
|
UIEvent::MailboxUpdate((self.hash, mailbox_hash)),
|
|
|
|
));
|
2020-12-24 21:56:24 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Ok(Some((Some(Err(err)), _))) => {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.set_job_success(job_id, false);
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
2023-11-26 20:36:02 +02:00
|
|
|
UIEvent::Notification {
|
|
|
|
title: Some(
|
|
|
|
format!("{}: could not fetch mailbox", &self.name).into(),
|
|
|
|
),
|
|
|
|
source: None,
|
|
|
|
body: err.to_string().into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Error(err.kind)),
|
|
|
|
},
|
2023-06-14 12:24:20 +03:00
|
|
|
));
|
2020-12-24 21:56:24 +02:00
|
|
|
self.mailbox_entries
|
|
|
|
.entry(mailbox_hash)
|
|
|
|
.and_modify(|entry| {
|
|
|
|
entry.status = MailboxStatus::Failed(err);
|
|
|
|
});
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
|
|
|
UIEvent::MailboxUpdate((self.hash, mailbox_hash)),
|
|
|
|
));
|
2020-12-24 21:56:24 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Ok(Some((Some(Ok(payload)), rest))) => {
|
|
|
|
let handle = if self.backend_capabilities.is_async {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
2023-07-14 00:23:24 +03:00
|
|
|
.spawn_specialized("rest_fetch".into(), rest.into_future())
|
2020-12-24 21:56:24 +02:00
|
|
|
} else {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
2023-07-14 00:23:24 +03:00
|
|
|
.spawn_blocking("rest_fetch".into(), rest.into_future())
|
2020-12-24 21:56:24 +02:00
|
|
|
};
|
|
|
|
self.insert_job(
|
|
|
|
handle.job_id,
|
|
|
|
JobRequest::Fetch {
|
|
|
|
mailbox_hash,
|
|
|
|
handle,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
let envelopes = payload
|
|
|
|
.into_iter()
|
|
|
|
.map(|e| (e.hash(), e))
|
|
|
|
.collect::<HashMap<EnvelopeHash, Envelope>>();
|
2024-03-16 14:57:48 +02:00
|
|
|
if let Some(updated_mailboxes) = self.collection.merge(
|
|
|
|
envelopes,
|
|
|
|
mailbox_hash,
|
|
|
|
self.settings.sent_mailbox,
|
|
|
|
) {
|
2020-12-24 21:56:24 +02:00
|
|
|
for f in updated_mailboxes {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
|
|
|
UIEvent::MailboxUpdate((self.hash, f)),
|
|
|
|
));
|
2020-12-24 21:56:24 +02:00
|
|
|
}
|
|
|
|
}
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
|
|
|
UIEvent::MailboxUpdate((self.hash, mailbox_hash)),
|
|
|
|
));
|
2020-06-27 21:40:46 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-10-09 19:34:55 +03:00
|
|
|
JobRequest::IsOnline { ref mut handle, .. } => {
|
2024-03-16 18:10:37 +02:00
|
|
|
if matches!(self.is_online, IsOnline::Err { ref value, ..} if !IsOnline::is_recoverable(value))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2020-12-24 21:56:24 +02:00
|
|
|
if let Ok(Some(is_online)) = handle.chan.try_recv() {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
|
|
|
UIEvent::AccountStatusChange(self.hash, None),
|
|
|
|
));
|
2023-06-19 00:13:53 +03:00
|
|
|
match is_online {
|
|
|
|
Ok(()) => {
|
2024-03-16 18:10:37 +02:00
|
|
|
if matches!(self.is_online, IsOnline::Err { .. })
|
2023-07-28 19:34:56 +03:00
|
|
|
|| matches!(self.is_online, IsOnline::Uninit)
|
2023-06-19 00:13:53 +03:00
|
|
|
{
|
|
|
|
self.watch();
|
|
|
|
}
|
|
|
|
self.is_online = IsOnline::True;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Err(value) => {
|
2024-03-16 18:10:37 +02:00
|
|
|
self.is_online.set_err(value);
|
2020-07-05 20:11:47 +03:00
|
|
|
}
|
2020-06-27 21:40:46 +03:00
|
|
|
}
|
|
|
|
}
|
2020-09-10 21:01:40 +03:00
|
|
|
let online_job = self.backend.read().unwrap().is_online();
|
|
|
|
if let Ok(online_job) = online_job {
|
2020-10-09 19:34:55 +03:00
|
|
|
let handle = if self.backend_capabilities.is_async {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
2023-07-14 00:23:24 +03:00
|
|
|
.spawn_specialized("is_online".into(), online_job)
|
2020-09-16 13:17:26 +03:00
|
|
|
} else {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
2023-07-14 00:23:24 +03:00
|
|
|
.spawn_blocking("is_online".into(), online_job)
|
2020-09-16 13:17:26 +03:00
|
|
|
};
|
2020-10-09 19:34:55 +03:00
|
|
|
self.insert_job(handle.job_id, JobRequest::IsOnline { handle });
|
2020-09-10 21:01:40 +03:00
|
|
|
};
|
2020-06-27 21:40:46 +03:00
|
|
|
}
|
2020-10-09 19:34:55 +03:00
|
|
|
JobRequest::Refresh { ref mut handle, .. } => {
|
2024-03-16 18:10:37 +02:00
|
|
|
if matches!(self.is_online, IsOnline::Err { ref value, ..} if !IsOnline::is_recoverable(value))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
2020-12-24 21:56:24 +02:00
|
|
|
match handle.chan.try_recv() {
|
|
|
|
Err(_) => { /* canceled */ }
|
|
|
|
Ok(None) => {}
|
|
|
|
Ok(Some(Ok(()))) => {
|
2024-03-16 18:10:37 +02:00
|
|
|
if matches!(self.is_online, IsOnline::Err { .. }) {
|
2020-07-08 00:26:40 +03:00
|
|
|
self.watch();
|
|
|
|
}
|
2023-06-19 00:13:53 +03:00
|
|
|
self.is_online = IsOnline::True;
|
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
|
|
|
UIEvent::AccountStatusChange(self.hash, None),
|
|
|
|
));
|
2020-07-05 20:11:47 +03:00
|
|
|
}
|
2020-12-24 21:56:24 +02:00
|
|
|
Ok(Some(Err(err))) => {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.set_job_success(job_id, false);
|
2023-06-19 00:13:53 +03:00
|
|
|
self.is_online.set_err(err);
|
|
|
|
_ = self.is_online();
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
|
|
|
UIEvent::AccountStatusChange(self.hash, None),
|
|
|
|
));
|
2020-08-02 00:46:37 +03:00
|
|
|
}
|
2020-06-29 00:18:24 +03:00
|
|
|
}
|
|
|
|
}
|
2023-11-24 12:58:21 +02:00
|
|
|
JobRequest::SetFlags {
|
|
|
|
ref mut handle,
|
|
|
|
ref env_hashes,
|
2023-11-27 09:36:13 +02:00
|
|
|
ref mailbox_hash,
|
2023-11-24 12:58:21 +02:00
|
|
|
ref flags,
|
|
|
|
} => match handle.chan.try_recv() {
|
|
|
|
Ok(Some(Err(err))) => {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.set_job_success(job_id, false);
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
2023-11-26 20:36:02 +02:00
|
|
|
.send(ThreadEvent::UIEvent(UIEvent::Notification {
|
|
|
|
title: Some(format!("{}: could not set flag", &self.name).into()),
|
|
|
|
source: None,
|
|
|
|
body: err.to_string().into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Error(err.kind)),
|
|
|
|
}));
|
2020-06-29 00:18:24 +03:00
|
|
|
}
|
2023-11-24 12:58:21 +02:00
|
|
|
Ok(Some(Ok(()))) => {
|
|
|
|
for env_hash in env_hashes.iter() {
|
|
|
|
if !self.collection.contains_key(&env_hash) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
let mut env_lck = self.collection.envelopes.write().unwrap();
|
|
|
|
env_lck.entry(env_hash).and_modify(|entry| {
|
|
|
|
for op in flags.iter() {
|
|
|
|
match op {
|
|
|
|
FlagOp::Set(f) => {
|
|
|
|
let mut flags = entry.flags();
|
|
|
|
flags.set(*f, true);
|
|
|
|
entry.set_flags(flags);
|
|
|
|
}
|
|
|
|
FlagOp::UnSet(f) => {
|
|
|
|
let mut flags = entry.flags();
|
|
|
|
flags.set(*f, false);
|
|
|
|
entry.set_flags(flags);
|
|
|
|
}
|
|
|
|
FlagOp::SetTag(t) => {
|
|
|
|
entry
|
|
|
|
.tags_mut()
|
|
|
|
.insert(TagHash::from_bytes(t.as_bytes()));
|
|
|
|
}
|
|
|
|
FlagOp::UnSetTag(t) => {
|
|
|
|
entry
|
|
|
|
.tags_mut()
|
|
|
|
.remove(&TagHash::from_bytes(t.as_bytes()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
#[cfg(feature = "sqlite3")]
|
|
|
|
if let Some(env) = env_lck.get(&env_hash).cloned() {
|
|
|
|
drop(env_lck);
|
|
|
|
self.update_cached_env(env, None);
|
|
|
|
}
|
|
|
|
}
|
2023-11-27 09:36:13 +02:00
|
|
|
for env_hash in env_hashes.iter() {
|
|
|
|
self.collection.update_flags(env_hash, *mailbox_hash);
|
2023-12-17 18:20:14 +02:00
|
|
|
self.main_loop_handler
|
|
|
|
.send(ThreadEvent::UIEvent(UIEvent::EnvelopeUpdate(env_hash)));
|
2023-11-27 09:36:13 +02:00
|
|
|
}
|
2023-11-24 12:58:21 +02:00
|
|
|
}
|
2023-11-27 09:36:13 +02:00
|
|
|
Err(_) | Ok(None) => {}
|
2023-11-24 12:58:21 +02:00
|
|
|
},
|
2020-08-26 20:00:25 +03:00
|
|
|
JobRequest::SaveMessage {
|
2020-10-09 19:34:55 +03:00
|
|
|
ref mut handle,
|
2020-08-26 20:00:25 +03:00
|
|
|
ref bytes,
|
|
|
|
..
|
|
|
|
} => {
|
2020-12-24 21:56:24 +02:00
|
|
|
if let Ok(Some(Err(err))) = handle.chan.try_recv() {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.set_job_success(job_id, false);
|
2023-05-01 16:22:35 +03:00
|
|
|
log::error!("Could not save message: {err}");
|
2023-11-26 19:28:11 +02:00
|
|
|
match crate::types::File::create_temp_file(
|
|
|
|
bytes,
|
|
|
|
None,
|
|
|
|
None,
|
|
|
|
Some("eml"),
|
|
|
|
false,
|
|
|
|
) {
|
|
|
|
Ok(file) => {
|
|
|
|
log::debug!("message saved in {}", file.path().display());
|
|
|
|
log::info!(
|
2020-08-26 20:00:25 +03:00
|
|
|
"Message was stored in {} so that you can restore it manually.",
|
2023-11-26 19:28:11 +02:00
|
|
|
file.path().display()
|
|
|
|
);
|
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
2023-11-26 20:36:02 +02:00
|
|
|
UIEvent::Notification {
|
|
|
|
title: Some(
|
|
|
|
format!("{}: could not save message", &self.name)
|
|
|
|
.into(),
|
|
|
|
),
|
|
|
|
source: None,
|
|
|
|
body: format!(
|
2023-11-26 19:28:11 +02:00
|
|
|
"Message was stored in {} so that you can restore it \
|
|
|
|
manually.",
|
|
|
|
file.path().display()
|
2023-11-26 20:36:02 +02:00
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Info),
|
|
|
|
},
|
2023-11-26 19:28:11 +02:00
|
|
|
));
|
|
|
|
}
|
|
|
|
Err(err) => log::error!("Could not save message: {err}"),
|
|
|
|
}
|
2020-06-29 00:18:24 +03:00
|
|
|
}
|
|
|
|
}
|
2020-09-10 21:01:40 +03:00
|
|
|
JobRequest::SendMessage => {}
|
2020-10-09 19:34:55 +03:00
|
|
|
JobRequest::SendMessageBackground { ref mut handle, .. } => {
|
2020-12-24 21:56:24 +02:00
|
|
|
if let Ok(Some(Err(err))) = handle.chan.try_recv() {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.set_job_success(job_id, false);
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
2023-11-26 20:36:02 +02:00
|
|
|
.send(ThreadEvent::UIEvent(UIEvent::Notification {
|
|
|
|
title: Some("Could not send message".into()),
|
|
|
|
source: None,
|
|
|
|
body: err.to_string().into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Error(err.kind)),
|
|
|
|
}));
|
Add smtp client support for sending mail in UI
`mailer_command` was removed, and a new setting `send_mail` was added.
Its possible values are a string, consisting of a shell command to
execute, or settings to configure an smtp server connection. The
configuration I used for testing this is:
[composing]
send_mail = { hostname = "smtp.mail.tld", port = 587, auth = { type = "auto", username = "yoshi", password = { type = "command_eval", value = "gpg2 --no-tty -q -d ~/.passwords/msmtp/yoshi.gpg" } }, security = { type = "STARTTLS" } }
For local smtp server:
[composing]
send_mail = { hostname = "localhost", port = 25, auth = { type = "none" }, security = { type = "none" } }
2020-07-15 14:38:43 +03:00
|
|
|
}
|
|
|
|
}
|
2020-10-09 19:34:55 +03:00
|
|
|
JobRequest::DeleteMessages { ref mut handle, .. } => {
|
2020-12-24 21:56:24 +02:00
|
|
|
if let Ok(Some(Err(err))) = handle.chan.try_recv() {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.set_job_success(job_id, false);
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
2023-11-26 20:36:02 +02:00
|
|
|
.send(ThreadEvent::UIEvent(UIEvent::Notification {
|
|
|
|
title: Some(
|
|
|
|
format!("{}: could not delete message", &self.name).into(),
|
|
|
|
),
|
|
|
|
source: None,
|
|
|
|
body: err.to_string().into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Error(err.kind)),
|
|
|
|
}));
|
2020-06-29 00:18:24 +03:00
|
|
|
}
|
|
|
|
}
|
2020-08-25 22:56:45 +03:00
|
|
|
JobRequest::CreateMailbox {
|
|
|
|
ref path,
|
2020-10-09 19:34:55 +03:00
|
|
|
ref mut handle,
|
2020-08-25 22:56:45 +03:00
|
|
|
..
|
|
|
|
} => {
|
2020-12-24 21:56:24 +02:00
|
|
|
if let Ok(Some(r)) = handle.chan.try_recv() {
|
2020-08-25 22:56:45 +03:00
|
|
|
match r {
|
|
|
|
Err(err) => {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.set_job_success(job_id, false);
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
2023-11-26 20:36:02 +02:00
|
|
|
UIEvent::Notification {
|
|
|
|
title: Some(
|
|
|
|
format!(
|
|
|
|
"{}: could not create mailbox {}",
|
|
|
|
&self.name, path
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
),
|
|
|
|
source: None,
|
|
|
|
body: err.to_string().into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Error(err.kind)),
|
|
|
|
},
|
2023-06-14 12:24:20 +03:00
|
|
|
));
|
2020-08-25 22:56:45 +03:00
|
|
|
}
|
|
|
|
Ok((mailbox_hash, mut mailboxes)) => {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
|
|
|
UIEvent::MailboxCreate((self.hash, mailbox_hash)),
|
|
|
|
));
|
2020-08-25 22:56:45 +03:00
|
|
|
let mut new = FileMailboxConf::default();
|
|
|
|
new.mailbox_conf.subscribe = super::ToggleFlag::InternalVal(true);
|
|
|
|
new.mailbox_conf.usage = if mailboxes[&mailbox_hash].special_usage()
|
|
|
|
!= SpecialUsageMailbox::Normal
|
|
|
|
{
|
|
|
|
Some(mailboxes[&mailbox_hash].special_usage())
|
|
|
|
} else {
|
|
|
|
let tmp = SpecialUsageMailbox::detect_usage(
|
|
|
|
mailboxes[&mailbox_hash].name(),
|
|
|
|
);
|
2020-12-25 06:09:46 +02:00
|
|
|
if let Some(tmp) =
|
|
|
|
tmp.filter(|&v| v != SpecialUsageMailbox::Normal)
|
|
|
|
{
|
2020-08-25 22:56:45 +03:00
|
|
|
mailboxes.entry(mailbox_hash).and_modify(|entry| {
|
2020-12-25 06:09:46 +02:00
|
|
|
let _ = entry.set_special_usage(tmp);
|
2020-08-25 22:56:45 +03:00
|
|
|
});
|
|
|
|
}
|
|
|
|
tmp
|
|
|
|
};
|
2023-04-30 19:39:41 +03:00
|
|
|
/* if new mailbox has parent, we need to update its children
|
|
|
|
* field */
|
2020-08-25 22:56:45 +03:00
|
|
|
if let Some(parent_hash) = mailboxes[&mailbox_hash].parent() {
|
|
|
|
self.mailbox_entries
|
|
|
|
.entry(parent_hash)
|
|
|
|
.and_modify(|parent| {
|
|
|
|
parent.ref_mailbox =
|
|
|
|
mailboxes.remove(&parent_hash).unwrap();
|
|
|
|
});
|
|
|
|
}
|
2020-08-27 17:29:27 +03:00
|
|
|
let status = MailboxStatus::default();
|
2020-08-25 22:56:45 +03:00
|
|
|
|
|
|
|
self.mailbox_entries.insert(
|
|
|
|
mailbox_hash,
|
2023-04-09 00:03:20 +03:00
|
|
|
MailboxEntry::new(
|
2020-08-25 22:56:45 +03:00
|
|
|
status,
|
2023-04-09 00:03:20 +03:00
|
|
|
mailboxes[&mailbox_hash].path().to_string(),
|
|
|
|
mailboxes.remove(&mailbox_hash).unwrap(),
|
|
|
|
new,
|
|
|
|
),
|
2020-08-25 22:56:45 +03:00
|
|
|
);
|
|
|
|
self.collection
|
|
|
|
.threads
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.insert(mailbox_hash, Threads::default());
|
|
|
|
self.collection
|
|
|
|
.mailboxes
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.insert(mailbox_hash, Default::default());
|
|
|
|
build_mailboxes_order(
|
|
|
|
&mut self.tree,
|
|
|
|
&self.mailbox_entries,
|
|
|
|
&mut self.mailboxes_order,
|
|
|
|
);
|
2023-04-30 19:39:41 +03:00
|
|
|
//Ok(format!("`{}` successfully created.",
|
|
|
|
// &path))
|
2020-08-25 22:56:45 +03:00
|
|
|
}
|
|
|
|
}
|
2020-06-29 00:18:24 +03:00
|
|
|
}
|
|
|
|
}
|
2020-08-25 22:56:45 +03:00
|
|
|
JobRequest::DeleteMailbox {
|
|
|
|
mailbox_hash,
|
2020-10-09 19:34:55 +03:00
|
|
|
ref mut handle,
|
2020-08-25 22:56:45 +03:00
|
|
|
..
|
|
|
|
} => {
|
2020-12-24 21:56:24 +02:00
|
|
|
match handle.chan.try_recv() {
|
|
|
|
Err(_) => { /* canceled */ }
|
|
|
|
Ok(None) => {}
|
|
|
|
Ok(Some(Err(err))) => {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.set_job_success(job_id, false);
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
2023-11-26 20:36:02 +02:00
|
|
|
UIEvent::Notification {
|
|
|
|
title: Some(
|
|
|
|
format!("{}: could not delete mailbox", &self.name).into(),
|
|
|
|
),
|
|
|
|
source: None,
|
|
|
|
body: err.to_string().into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Error(err.kind)),
|
|
|
|
},
|
2023-06-14 12:24:20 +03:00
|
|
|
));
|
2020-06-29 00:18:24 +03:00
|
|
|
}
|
2020-12-24 21:56:24 +02:00
|
|
|
Ok(Some(Ok(mut mailboxes))) => {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
|
|
|
UIEvent::MailboxDelete((self.hash, mailbox_hash)),
|
|
|
|
));
|
2020-08-25 22:56:45 +03:00
|
|
|
if let Some(pos) =
|
|
|
|
self.mailboxes_order.iter().position(|&h| h == mailbox_hash)
|
|
|
|
{
|
|
|
|
self.mailboxes_order.remove(pos);
|
|
|
|
}
|
|
|
|
if let Some(pos) = self.tree.iter().position(|n| n.hash == mailbox_hash)
|
|
|
|
{
|
|
|
|
self.tree.remove(pos);
|
|
|
|
}
|
2024-03-16 14:57:48 +02:00
|
|
|
if self.settings.sent_mailbox == Some(mailbox_hash) {
|
|
|
|
self.settings.sent_mailbox = None;
|
2020-08-25 22:56:45 +03:00
|
|
|
}
|
|
|
|
self.collection
|
|
|
|
.threads
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.remove(&mailbox_hash);
|
|
|
|
let deleted_mailbox =
|
|
|
|
self.mailbox_entries.remove(&mailbox_hash).unwrap();
|
2023-04-30 19:39:41 +03:00
|
|
|
/* if deleted mailbox had parent, we need to update its children
|
|
|
|
* field */
|
2020-08-25 22:56:45 +03:00
|
|
|
if let Some(parent_hash) = deleted_mailbox.ref_mailbox.parent() {
|
|
|
|
self.mailbox_entries
|
|
|
|
.entry(parent_hash)
|
|
|
|
.and_modify(|parent| {
|
|
|
|
parent.ref_mailbox =
|
|
|
|
mailboxes.remove(&parent_hash).unwrap();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
self.collection
|
|
|
|
.mailboxes
|
|
|
|
.write()
|
|
|
|
.unwrap()
|
|
|
|
.remove(&mailbox_hash);
|
|
|
|
build_mailboxes_order(
|
|
|
|
&mut self.tree,
|
|
|
|
&self.mailbox_entries,
|
|
|
|
&mut self.mailboxes_order,
|
|
|
|
);
|
2023-07-13 16:47:11 +03:00
|
|
|
// [ref:FIXME] remove from settings as well
|
2020-08-25 22:56:45 +03:00
|
|
|
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
2023-11-26 20:36:02 +02:00
|
|
|
UIEvent::Notification {
|
|
|
|
title: Some(
|
|
|
|
format!("{}: mailbox deleted successfully", &self.name)
|
|
|
|
.into(),
|
|
|
|
),
|
|
|
|
source: None,
|
|
|
|
body: "".into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Info),
|
|
|
|
},
|
2023-06-14 12:24:20 +03:00
|
|
|
));
|
2020-06-29 00:18:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//JobRequest::RenameMailbox,
|
2020-10-09 19:34:55 +03:00
|
|
|
JobRequest::SetMailboxPermissions { ref mut handle, .. } => {
|
2020-12-24 21:56:24 +02:00
|
|
|
match handle.chan.try_recv() {
|
|
|
|
Err(_) => { /* canceled */ }
|
|
|
|
Ok(None) => {}
|
|
|
|
Ok(Some(Err(err))) => {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.set_job_success(job_id, false);
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
2023-11-26 20:36:02 +02:00
|
|
|
UIEvent::Notification {
|
|
|
|
title: Some(
|
|
|
|
format!(
|
|
|
|
"{}: could not set mailbox permissions",
|
|
|
|
&self.name
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
),
|
|
|
|
source: None,
|
|
|
|
body: err.to_string().into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Error(err.kind)),
|
|
|
|
},
|
2023-06-14 12:24:20 +03:00
|
|
|
));
|
2020-06-29 00:18:24 +03:00
|
|
|
}
|
2020-12-24 21:56:24 +02:00
|
|
|
Ok(Some(Ok(_))) => {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
2023-11-26 20:36:02 +02:00
|
|
|
UIEvent::Notification {
|
|
|
|
title: Some(
|
|
|
|
format!(
|
|
|
|
"{}: mailbox permissions set successfully",
|
|
|
|
&self.name
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
),
|
|
|
|
source: None,
|
|
|
|
body: "".into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Info),
|
|
|
|
},
|
2023-06-14 12:24:20 +03:00
|
|
|
));
|
2020-06-29 00:18:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-11-28 15:12:54 +02:00
|
|
|
JobRequest::SetMailboxSubscription {
|
|
|
|
ref mut handle,
|
|
|
|
ref mailbox_hash,
|
|
|
|
ref new_value,
|
|
|
|
} => {
|
2020-12-24 21:56:24 +02:00
|
|
|
match handle.chan.try_recv() {
|
|
|
|
Err(_) => { /* canceled */ }
|
|
|
|
Ok(None) => {}
|
|
|
|
Ok(Some(Err(err))) => {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.set_job_success(job_id, false);
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
2023-11-26 20:36:02 +02:00
|
|
|
UIEvent::Notification {
|
|
|
|
title: Some(
|
|
|
|
format!(
|
|
|
|
"{}: could not set mailbox subscription",
|
|
|
|
&self.name
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
),
|
|
|
|
source: None,
|
|
|
|
body: err.to_string().into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Error(err.kind)),
|
|
|
|
},
|
2023-06-14 12:24:20 +03:00
|
|
|
));
|
2020-06-29 00:18:24 +03:00
|
|
|
}
|
2022-11-28 15:12:54 +02:00
|
|
|
Ok(Some(Ok(()))) if self.mailbox_entries.contains_key(mailbox_hash) => {
|
|
|
|
self.mailbox_entries.entry(*mailbox_hash).and_modify(|m| {
|
|
|
|
m.conf.mailbox_conf.subscribe = if *new_value {
|
|
|
|
super::ToggleFlag::True
|
|
|
|
} else {
|
|
|
|
super::ToggleFlag::False
|
|
|
|
};
|
|
|
|
let _ = m.ref_mailbox.set_is_subscribed(*new_value);
|
|
|
|
});
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
2023-11-26 20:36:02 +02:00
|
|
|
UIEvent::Notification {
|
|
|
|
title: Some(
|
|
|
|
format!(
|
|
|
|
"{}: `{}` has been {}subscribed.",
|
|
|
|
&self.name,
|
|
|
|
self.mailbox_entries[mailbox_hash].name(),
|
|
|
|
if *new_value { "" } else { "un" }
|
|
|
|
)
|
|
|
|
.into(),
|
|
|
|
),
|
|
|
|
source: None,
|
|
|
|
body: "".into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Info),
|
|
|
|
},
|
2023-06-14 12:24:20 +03:00
|
|
|
));
|
2020-06-29 00:18:24 +03:00
|
|
|
}
|
2022-11-28 15:12:54 +02:00
|
|
|
Ok(Some(Ok(()))) => {}
|
2020-06-29 00:18:24 +03:00
|
|
|
}
|
|
|
|
}
|
2020-10-09 19:34:55 +03:00
|
|
|
JobRequest::Watch { ref mut handle } => {
|
2023-06-16 20:20:12 +03:00
|
|
|
log::trace!("JobRequest::Watch finished??? ");
|
2020-12-24 21:56:24 +02:00
|
|
|
if let Ok(Some(Err(err))) = handle.chan.try_recv() {
|
2020-08-26 20:01:39 +03:00
|
|
|
if err.kind.is_timeout() {
|
|
|
|
self.watch();
|
|
|
|
} else {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.set_job_success(job_id, false);
|
2023-07-13 16:47:11 +03:00
|
|
|
// [ref:TODO]: relaunch watch job with ratelimit for failure
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
2023-11-26 20:36:02 +02:00
|
|
|
UIEvent::Notification {
|
|
|
|
title: Some(
|
|
|
|
format!("{}: watch thread failed", &self.name).into(),
|
|
|
|
),
|
|
|
|
source: None,
|
|
|
|
body: err.to_string().into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Error(err.kind)),
|
|
|
|
},
|
2023-06-14 12:24:20 +03:00
|
|
|
));
|
2020-08-26 20:01:39 +03:00
|
|
|
}
|
2020-08-25 21:12:28 +03:00
|
|
|
}
|
2020-07-05 13:22:48 +03:00
|
|
|
}
|
2020-07-16 22:54:50 +03:00
|
|
|
JobRequest::Generic {
|
2020-07-29 14:27:43 +03:00
|
|
|
ref name,
|
2020-10-09 19:34:55 +03:00
|
|
|
ref mut handle,
|
2020-08-25 15:39:43 +03:00
|
|
|
ref mut on_finish,
|
2023-05-01 16:22:35 +03:00
|
|
|
log_level,
|
2020-07-16 22:54:50 +03:00
|
|
|
} => {
|
2020-12-24 21:56:24 +02:00
|
|
|
match handle.chan.try_recv() {
|
|
|
|
Ok(Some(Err(err))) => {
|
2023-07-14 00:23:24 +03:00
|
|
|
self.main_loop_handler
|
|
|
|
.job_executor
|
|
|
|
.set_job_success(job_id, false);
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
2023-11-26 20:36:02 +02:00
|
|
|
UIEvent::Notification {
|
|
|
|
title: Some(format!("{}: {} failed", &self.name, name,).into()),
|
|
|
|
source: None,
|
|
|
|
body: err.to_string().into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Error(err.kind)),
|
|
|
|
},
|
2023-06-14 12:24:20 +03:00
|
|
|
));
|
2020-07-16 22:54:50 +03:00
|
|
|
}
|
2020-12-24 21:56:24 +02:00
|
|
|
Ok(Some(Ok(()))) if on_finish.is_none() => {
|
2023-05-01 16:22:35 +03:00
|
|
|
if log_level <= LogLevel::INFO {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler.send(ThreadEvent::UIEvent(
|
2023-11-26 20:36:02 +02:00
|
|
|
UIEvent::Notification {
|
|
|
|
title: Some(
|
|
|
|
format!("{}: {} succeeded", &self.name, name,).into(),
|
|
|
|
),
|
|
|
|
source: None,
|
|
|
|
body: "".into(),
|
|
|
|
kind: Some(crate::types::NotificationType::Info),
|
|
|
|
},
|
2023-06-14 12:24:20 +03:00
|
|
|
));
|
2020-08-26 19:15:31 +03:00
|
|
|
}
|
2020-07-16 22:54:50 +03:00
|
|
|
}
|
2020-12-24 21:56:24 +02:00
|
|
|
Err(_) => { /* canceled */ }
|
|
|
|
Ok(Some(Ok(()))) | Ok(None) => {}
|
2020-07-16 22:54:50 +03:00
|
|
|
}
|
2020-08-25 15:39:43 +03:00
|
|
|
if on_finish.is_some() {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
2020-08-25 15:39:43 +03:00
|
|
|
.send(ThreadEvent::UIEvent(UIEvent::Callback(
|
|
|
|
on_finish.take().unwrap(),
|
2023-06-14 12:24:20 +03:00
|
|
|
)));
|
2020-08-25 15:39:43 +03:00
|
|
|
}
|
2020-07-16 22:54:50 +03:00
|
|
|
}
|
2020-06-27 21:40:46 +03:00
|
|
|
}
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2020-07-24 21:48:29 +03:00
|
|
|
|
|
|
|
pub fn insert_job(&mut self, job_id: JobId, job: JobRequest) {
|
|
|
|
self.active_jobs.insert(job_id, job);
|
|
|
|
self.active_job_instants
|
|
|
|
.insert(std::time::Instant::now(), job_id);
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
2020-09-10 21:01:40 +03:00
|
|
|
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
|
|
|
StatusEvent::NewJob(job_id),
|
2023-06-14 12:24:20 +03:00
|
|
|
)));
|
2020-07-24 21:48:29 +03:00
|
|
|
}
|
2020-09-14 19:32:43 +03:00
|
|
|
|
|
|
|
pub fn cancel_job(&mut self, job_id: JobId) -> Option<JobRequest> {
|
|
|
|
if let Some(req) = self.active_jobs.remove(&job_id) {
|
2023-06-14 12:24:20 +03:00
|
|
|
self.main_loop_handler
|
2020-09-14 19:32:43 +03:00
|
|
|
.send(ThreadEvent::UIEvent(UIEvent::StatusEvent(
|
|
|
|
StatusEvent::JobCanceled(job_id),
|
2023-06-14 12:24:20 +03:00
|
|
|
)));
|
2020-09-14 19:32:43 +03:00
|
|
|
Some(req)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
2018-08-03 13:46:08 +03:00
|
|
|
}
|
2019-05-26 02:34:03 +03:00
|
|
|
|
2020-02-26 10:54:10 +02:00
|
|
|
impl Index<&MailboxHash> for Account {
|
|
|
|
type Output = MailboxEntry;
|
|
|
|
fn index(&self, index: &MailboxHash) -> &MailboxEntry {
|
|
|
|
&self.mailbox_entries[index]
|
2019-04-14 17:24:01 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-26 10:54:10 +02:00
|
|
|
impl IndexMut<&MailboxHash> for Account {
|
|
|
|
fn index_mut(&mut self, index: &MailboxHash) -> &mut MailboxEntry {
|
|
|
|
self.mailbox_entries.get_mut(index).unwrap()
|
2019-04-14 17:24:01 +03:00
|
|
|
}
|
|
|
|
}
|