parent
1245eae0be
commit
4ac52d9d5b
30
meli.1
30
meli.1
|
@ -94,7 +94,7 @@ The menu's visibility may be toggled with
|
|||
.Ic toggle_menu_visibility Ns
|
||||
).
|
||||
.Pp
|
||||
The view into each folder has 4 modes: plain, threaded, conversations and compact.
|
||||
The view into each mailbox has 4 modes: plain, threaded, conversations and compact.
|
||||
Plain views each mail indvidually, threaded shows their thread relationship visually, and conversations includes one entry per thread of emails (compact is one row per thread).
|
||||
.Pp
|
||||
If you're using a light color palette in your terminal, you may set
|
||||
|
@ -224,9 +224,9 @@ To save your draft without sending it, issue command
|
|||
.Cm close
|
||||
and select 'save as draft'.
|
||||
.Pp
|
||||
With no Draft or Sent folder,
|
||||
With no Draft or Sent mailbox,
|
||||
.Nm
|
||||
tries first saving mail in your INBOX and then at any other folder.
|
||||
tries first saving mail in your INBOX and then at any other mailbox.
|
||||
On complete failure to save your draft or sent message it will be saved in your
|
||||
.Em tmp
|
||||
directory instead and you will be notified of its location.
|
||||
|
@ -307,17 +307,17 @@ filter mailbox with
|
|||
key.
|
||||
Escape exits filter results
|
||||
.It Cm set read, set unread
|
||||
.It Cm create-folder Ar ACCOUNT Ar FOLDER_PATH
|
||||
create folder with given path.
|
||||
.It Cm create-mailbox Ar ACCOUNT Ar MAILBOX_PATH
|
||||
create mailbox with given path.
|
||||
Be careful with backends and separator sensitivity (eg IMAP)
|
||||
.It Cm subscribe-folder Ar ACCOUNT Ar FOLDER_PATH
|
||||
subscribe to folder with given path
|
||||
.It Cm unsubscribe-folder Ar ACCOUNT Ar FOLDER_PATH
|
||||
unsubscribe to folder with given path
|
||||
.It Cm rename-folder Ar ACCOUNT Ar FOLDER_PATH_SRC Ar FOLDER_PATH_DEST
|
||||
rename folder
|
||||
.It Cm delete-folder Ar ACCOUNT Ar FOLDER_PATH
|
||||
delete folder
|
||||
.It Cm subscribe-mailbox Ar ACCOUNT Ar MAILBOX_PATH
|
||||
subscribe to mailbox with given path
|
||||
.It Cm unsubscribe-mailbox Ar ACCOUNT Ar MAILBOX_PATH
|
||||
unsubscribe to mailbox with given path
|
||||
.It Cm rename-mailbox Ar ACCOUNT Ar MAILBOX_PATH_SRC Ar MAILBOX_PATH_DEST
|
||||
rename mailbox
|
||||
.It Cm delete-mailbox Ar ACCOUNT Ar MAILBOX_PATH
|
||||
delete mailbox
|
||||
.El
|
||||
.Pp
|
||||
envelope view commands:
|
||||
|
@ -376,9 +376,9 @@ Non-complete list of shortcuts and their default values.
|
|||
PageUp,
|
||||
.It Ic next_page
|
||||
PageDown
|
||||
.It Ic prev_folder
|
||||
.It Ic prev_mailbox
|
||||
\&'K'
|
||||
.It Ic next_folder
|
||||
.It Ic next_mailbox
|
||||
\&'J'
|
||||
.It Ic prev_account
|
||||
\&'l'
|
||||
|
|
76
meli.conf.5
76
meli.conf.5
|
@ -56,23 +56,23 @@ example configuration
|
|||
.Bd -literal
|
||||
# Setting up a Maildir account
|
||||
[accounts.account-name]
|
||||
root_folder = "/path/to/root/folder"
|
||||
root_mailbox = "/path/to/root/folder"
|
||||
format = "Maildir"
|
||||
index_style = "Compact"
|
||||
identity="email@address.tld"
|
||||
subscribed_folders = ["folder", "folder/Sent"] # or [ "*", ] for all folders
|
||||
subscribed_mailboxes = ["folder", "folder/Sent"] # or [ "*", ] for all mailboxes
|
||||
display_name = "Name"
|
||||
|
||||
# Set folder-specific settings
|
||||
[accounts.account-name.folders]
|
||||
# Set mailbox-specific settings
|
||||
[accounts.account-name.mailboxes]
|
||||
"INBOX" = { alias="Inbox" } #inline table
|
||||
"drafts" = { alias="Drafts" } #inline table
|
||||
[accounts.account-name.folders."foobar-devel"] # or a regular table
|
||||
ignore = true # don't show notifications for this folder
|
||||
[accounts.account-name.mailboxes."foobar-devel"] # or a regular table
|
||||
ignore = true # don't show notifications for this mailbox
|
||||
|
||||
# Setting up an mbox account
|
||||
[accounts.mbox]
|
||||
root_folder = "/var/mail/username"
|
||||
root_mailbox = "/var/mail/username"
|
||||
format = "mbox"
|
||||
index_style = "Compact"
|
||||
identity="username@hostname.local"
|
||||
|
@ -105,16 +105,16 @@ available options are listed below.
|
|||
.Sy default values are shown in parentheses.
|
||||
.Sh ACCOUNTS
|
||||
.Bl -tag -width 36n
|
||||
.It Ic root_folder Ar String
|
||||
the backend-specific path of the root_folder, usually INBOX.
|
||||
.It Ic root_mailbox Ar String
|
||||
the backend-specific path of the root_mailbox, usually INBOX.
|
||||
.It Ic format Ar String Op maildir mbox imap notmuch jmap
|
||||
the format of the mail backend.
|
||||
.It Ic subscribed_folders Ar [String,]
|
||||
an array of folder paths to display in the UI.
|
||||
Paths are relative to the root folder (eg "INBOX/Sent", not "Sent").
|
||||
.It Ic subscribed_mailboxes Ar [String,]
|
||||
an array of mailbox paths to display in the UI.
|
||||
Paths are relative to the root mailbox (eg "INBOX/Sent", not "Sent").
|
||||
The glob wildcard
|
||||
.Em \&*
|
||||
can be used to match every folder name and path.
|
||||
can be used to match every mailbox name and path.
|
||||
.It Ic identity Ar String
|
||||
your e-mail address that is inserted in the From: headers of outgoing mail
|
||||
.It Ic index_style Ar String
|
||||
|
@ -148,20 +148,20 @@ Available options are 'none' and 'sqlite3'
|
|||
.It Ic vcard_folder Ar String
|
||||
(optional) Folder that contains .vcf files.
|
||||
They are parsed and imported read-only.
|
||||
.It Ic folders Ar folder_config
|
||||
(optional) configuration for each folder.
|
||||
.It Ic mailboxes Ar mailbox
|
||||
(optional) configuration for each mailbox.
|
||||
Its format is described below in
|
||||
.Sx FOLDERS Ns
|
||||
.Sx mailboxes Ns
|
||||
\&.
|
||||
.El
|
||||
.Sh notmuch only
|
||||
.Ic root_folder
|
||||
.Ic root_mailbox
|
||||
points to the directory which contains the
|
||||
.Pa .notmuch/
|
||||
subdirectory.
|
||||
notmuch folders are virtual, since they are defined by user-given notmuch queries.
|
||||
Thus you have to explicitly state the folders you want in the
|
||||
.Ic folders
|
||||
notmuch mailboxes are virtual, since they are defined by user-given notmuch queries.
|
||||
Thus you have to explicitly state the mailboxes you want in the
|
||||
.Ic mailboxes
|
||||
field and set the
|
||||
.Ar query
|
||||
property to each of them.
|
||||
|
@ -170,7 +170,7 @@ Example:
|
|||
[accounts.notmuch]
|
||||
format = "notmuch"
|
||||
\&...
|
||||
[accounts.notmuch.folders]
|
||||
[accounts.notmuch.mailboxes]
|
||||
"INBOX" = { query="tag:inbox", subscribe = true }
|
||||
"Drafts" = { query="tag:draft", subscribe = true }
|
||||
"Sent" = { query="from:username@server.tld from:username2@server.tld", subscribe = true }
|
||||
|
@ -213,22 +213,22 @@ example:
|
|||
.\" default value
|
||||
.Pq Em false
|
||||
.El
|
||||
.Sh FOLDERS
|
||||
.Sh mailboxes
|
||||
.Bl -tag -width 36n
|
||||
.It Ic alias Ar String
|
||||
(optional) show a different name for this folder in the UI
|
||||
(optional) show a different name for this mailbox in the UI
|
||||
.It Ic autoload Ar boolean
|
||||
(optional) load this folder on startup (not functional yet)
|
||||
(optional) load this mailbox on startup (not functional yet)
|
||||
.It Ic subscribe Ar boolean
|
||||
(optional) watch this folder for updates
|
||||
(optional) watch this mailbox for updates
|
||||
.\" default value
|
||||
.Pq Em true
|
||||
.It Ic ignore Ar boolean
|
||||
(optional) silently insert updates for this folder, if any
|
||||
(optional) silently insert updates for this mailbox, if any
|
||||
.\" default value
|
||||
.Pq Em false
|
||||
.It Ic usage Ar boolean
|
||||
(optional) special usage of this folder.
|
||||
(optional) special usage of this mailbox.
|
||||
Valid values are:
|
||||
.Bl -bullet -compact
|
||||
.It
|
||||
|
@ -248,9 +248,9 @@ Valid values are:
|
|||
.It
|
||||
.Ar Trash
|
||||
.El
|
||||
otherwise usage is inferred from the folder title.
|
||||
otherwise usage is inferred from the mailbox title.
|
||||
.It Ic conf_override Ar boolean
|
||||
(optional) override global settings for this folder.
|
||||
(optional) override global settings for this mailbox.
|
||||
Available sections to override are
|
||||
.Em pager, notifications, shortcuts, composing
|
||||
and the account options
|
||||
|
@ -258,9 +258,9 @@ and the account options
|
|||
\&.
|
||||
Example:
|
||||
.Bd -literal
|
||||
[accounts."imap.domain.tld".folders."INBOX"]
|
||||
[accounts."imap.domain.tld".mailboxes."INBOX"]
|
||||
index_style = "plain"
|
||||
[accounts."imap.domain.tld".folders."INBOX".pager]
|
||||
[accounts."imap.domain.tld".mailboxes."INBOX".pager]
|
||||
filter = ""
|
||||
.Ed
|
||||
.El
|
||||
|
@ -347,12 +347,12 @@ Go to previous page.
|
|||
Go to next page.
|
||||
.\" default value
|
||||
.Pq Em PageDown
|
||||
.It Ic prev_folder
|
||||
Go to previous folder.
|
||||
.It Ic prev_mailbox
|
||||
Go to previous mailbox.
|
||||
.\" default value
|
||||
.Pq Em K
|
||||
.It Ic next_folder
|
||||
Go to next folder.
|
||||
.It Ic next_mailbox
|
||||
Go to next mailbox.
|
||||
.\" default value
|
||||
.Pq Em J
|
||||
.It Ic prev_account
|
||||
|
@ -372,7 +372,7 @@ Set thread as seen.
|
|||
.\" default value
|
||||
.Pq Em n
|
||||
.It Ic refresh
|
||||
Manually request a folder refresh.
|
||||
Manually request a mailbox refresh.
|
||||
.\" default value
|
||||
.Pq Em F5
|
||||
.It Ic search
|
||||
|
@ -622,8 +622,8 @@ example configuration:
|
|||
colors = { signed="#Ff6600", replied="DeepSkyBlue4", draft="#f00", replied="8" }
|
||||
[accounts.dummy]
|
||||
\&...
|
||||
[accounts.dummy.folders]
|
||||
# per folder override:
|
||||
[accounts.dummy.mailboxes]
|
||||
# per mailbox override:
|
||||
"INBOX" = { tags.ignore_tags=["inbox", ] }
|
||||
.Ed
|
||||
.Sh PGP
|
||||
|
|
|
@ -193,12 +193,12 @@ pub enum RefreshEventKind {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct RefreshEvent {
|
||||
hash: FolderHash,
|
||||
hash: MailboxHash,
|
||||
kind: RefreshEventKind,
|
||||
}
|
||||
|
||||
impl RefreshEvent {
|
||||
pub fn hash(&self) -> FolderHash {
|
||||
pub fn hash(&self) -> MailboxHash {
|
||||
self.hash
|
||||
}
|
||||
pub fn kind(self) -> RefreshEventKind {
|
||||
|
@ -220,7 +220,7 @@ impl RefreshEventConsumer {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct NotifyFn(Box<dyn Fn(FolderHash) -> () + Send + Sync>);
|
||||
pub struct NotifyFn(Box<dyn Fn(MailboxHash) -> () + Send + Sync>);
|
||||
|
||||
impl fmt::Debug for NotifyFn {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
|
@ -228,17 +228,17 @@ impl fmt::Debug for NotifyFn {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Box<dyn Fn(FolderHash) -> () + Send + Sync>> for NotifyFn {
|
||||
fn from(kind: Box<dyn Fn(FolderHash) -> () + Send + Sync>) -> Self {
|
||||
impl From<Box<dyn Fn(MailboxHash) -> () + Send + Sync>> for NotifyFn {
|
||||
fn from(kind: Box<dyn Fn(MailboxHash) -> () + Send + Sync>) -> Self {
|
||||
NotifyFn(kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl NotifyFn {
|
||||
pub fn new(b: Box<dyn Fn(FolderHash) -> () + Send + Sync>) -> Self {
|
||||
pub fn new(b: Box<dyn Fn(MailboxHash) -> () + Send + Sync>) -> Self {
|
||||
NotifyFn(b)
|
||||
}
|
||||
pub fn notify(&self, f: FolderHash) {
|
||||
pub fn notify(&self, f: MailboxHash) {
|
||||
self.0(f);
|
||||
}
|
||||
}
|
||||
|
@ -246,10 +246,10 @@ impl NotifyFn {
|
|||
pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
|
||||
fn is_online(&self) -> Result<()>;
|
||||
fn connect(&mut self) {}
|
||||
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>>;
|
||||
fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>>;
|
||||
fn refresh(
|
||||
&mut self,
|
||||
_folder_hash: FolderHash,
|
||||
_mailbox_hash: MailboxHash,
|
||||
_sender: RefreshEventConsumer,
|
||||
) -> Result<Async<()>> {
|
||||
Err(MeliError::new("Unimplemented."))
|
||||
|
@ -259,10 +259,10 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
|
|||
sender: RefreshEventConsumer,
|
||||
work_context: WorkContext,
|
||||
) -> Result<std::thread::ThreadId>;
|
||||
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>>;
|
||||
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>>;
|
||||
fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp>;
|
||||
|
||||
fn save(&self, bytes: &[u8], folder: &str, flags: Option<Flag>) -> Result<()>;
|
||||
fn save(&self, bytes: &[u8], mailbox: &str, flags: Option<Flag>) -> Result<()>;
|
||||
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
|
||||
None
|
||||
}
|
||||
|
@ -272,32 +272,32 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
fn create_folder(
|
||||
fn create_mailbox(
|
||||
&mut self,
|
||||
_path: String,
|
||||
) -> Result<(FolderHash, FnvHashMap<FolderHash, Folder>)> {
|
||||
) -> Result<(MailboxHash, FnvHashMap<MailboxHash, Mailbox>)> {
|
||||
Err(MeliError::new("Unimplemented."))
|
||||
}
|
||||
|
||||
fn delete_folder(
|
||||
fn delete_mailbox(
|
||||
&mut self,
|
||||
_folder_hash: FolderHash,
|
||||
) -> Result<FnvHashMap<FolderHash, Folder>> {
|
||||
_mailbox_hash: MailboxHash,
|
||||
) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||
Err(MeliError::new("Unimplemented."))
|
||||
}
|
||||
|
||||
fn set_folder_subscription(&mut self, _folder_hash: FolderHash, _val: bool) -> Result<()> {
|
||||
fn set_mailbox_subscription(&mut self, _mailbox_hash: MailboxHash, _val: bool) -> Result<()> {
|
||||
Err(MeliError::new("Unimplemented."))
|
||||
}
|
||||
|
||||
fn rename_folder(&mut self, _folder_hash: FolderHash, _new_path: String) -> Result<Folder> {
|
||||
fn rename_mailbox(&mut self, _mailbox_hash: MailboxHash, _new_path: String) -> Result<Mailbox> {
|
||||
Err(MeliError::new("Unimplemented."))
|
||||
}
|
||||
|
||||
fn set_folder_permissions(
|
||||
fn set_mailbox_permissions(
|
||||
&mut self,
|
||||
_folder_hash: FolderHash,
|
||||
_val: FolderPermissions,
|
||||
_mailbox_hash: MailboxHash,
|
||||
_val: MailboxPermissions,
|
||||
) -> Result<()> {
|
||||
Err(MeliError::new("Unimplemented."))
|
||||
}
|
||||
|
@ -315,7 +315,7 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
|
|||
/// ```ignore
|
||||
/// /* Create operation from Backend */
|
||||
///
|
||||
/// let op = backend.operation(message.hash(), mailbox.folder.hash());
|
||||
/// let op = backend.operation(message.hash(), mailbox.hash());
|
||||
/// ```
|
||||
///
|
||||
/// # Example
|
||||
|
@ -443,30 +443,30 @@ impl SpecialUsageMailbox {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait BackendFolder: Debug {
|
||||
fn hash(&self) -> FolderHash;
|
||||
pub trait BackendMailbox: Debug {
|
||||
fn hash(&self) -> MailboxHash;
|
||||
fn name(&self) -> &str;
|
||||
/// Path of folder within the mailbox hierarchy, with `/` as separator.
|
||||
/// Path of mailbox within the mailbox hierarchy, with `/` as separator.
|
||||
fn path(&self) -> &str;
|
||||
fn change_name(&mut self, new_name: &str);
|
||||
fn clone(&self) -> Folder;
|
||||
fn children(&self) -> &[FolderHash];
|
||||
fn parent(&self) -> Option<FolderHash>;
|
||||
fn clone(&self) -> Mailbox;
|
||||
fn children(&self) -> &[MailboxHash];
|
||||
fn parent(&self) -> Option<MailboxHash>;
|
||||
fn is_subscribed(&self) -> bool;
|
||||
fn set_is_subscribed(&mut self, new_val: bool) -> Result<()>;
|
||||
fn set_special_usage(&mut self, new_val: SpecialUsageMailbox) -> Result<()>;
|
||||
fn special_usage(&self) -> SpecialUsageMailbox;
|
||||
fn permissions(&self) -> FolderPermissions;
|
||||
fn permissions(&self) -> MailboxPermissions;
|
||||
fn count(&self) -> Result<(usize, usize)>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DummyFolder {
|
||||
v: Vec<FolderHash>,
|
||||
struct DummyMailbox {
|
||||
v: Vec<MailboxHash>,
|
||||
}
|
||||
|
||||
impl BackendFolder for DummyFolder {
|
||||
fn hash(&self) -> FolderHash {
|
||||
impl BackendMailbox for DummyMailbox {
|
||||
fn hash(&self) -> MailboxHash {
|
||||
0
|
||||
}
|
||||
|
||||
|
@ -480,24 +480,24 @@ impl BackendFolder for DummyFolder {
|
|||
|
||||
fn change_name(&mut self, _s: &str) {}
|
||||
|
||||
fn clone(&self) -> Folder {
|
||||
folder_default()
|
||||
fn clone(&self) -> Mailbox {
|
||||
mailbox_default()
|
||||
}
|
||||
|
||||
fn special_usage(&self) -> SpecialUsageMailbox {
|
||||
SpecialUsageMailbox::Normal
|
||||
}
|
||||
|
||||
fn children(&self) -> &[FolderHash] {
|
||||
fn children(&self) -> &[MailboxHash] {
|
||||
&self.v
|
||||
}
|
||||
|
||||
fn parent(&self) -> Option<FolderHash> {
|
||||
fn parent(&self) -> Option<MailboxHash> {
|
||||
None
|
||||
}
|
||||
|
||||
fn permissions(&self) -> FolderPermissions {
|
||||
FolderPermissions::default()
|
||||
fn permissions(&self) -> MailboxPermissions {
|
||||
MailboxPermissions::default()
|
||||
}
|
||||
fn is_subscribed(&self) -> bool {
|
||||
true
|
||||
|
@ -513,29 +513,29 @@ impl BackendFolder for DummyFolder {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn folder_default() -> Folder {
|
||||
Box::new(DummyFolder {
|
||||
pub fn mailbox_default() -> Mailbox {
|
||||
Box::new(DummyMailbox {
|
||||
v: Vec::with_capacity(0),
|
||||
})
|
||||
}
|
||||
|
||||
pub type FolderHash = u64;
|
||||
pub type Folder = Box<dyn BackendFolder + Send + Sync>;
|
||||
pub type MailboxHash = u64;
|
||||
pub type Mailbox = Box<dyn BackendMailbox + Send + Sync>;
|
||||
|
||||
impl Clone for Folder {
|
||||
impl Clone for Mailbox {
|
||||
fn clone(&self) -> Self {
|
||||
BackendFolder::clone(self.deref())
|
||||
BackendMailbox::clone(self.deref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Folder {
|
||||
impl Default for Mailbox {
|
||||
fn default() -> Self {
|
||||
folder_default()
|
||||
mailbox_default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||
pub struct FolderPermissions {
|
||||
pub struct MailboxPermissions {
|
||||
pub create_messages: bool,
|
||||
pub remove_messages: bool,
|
||||
pub set_flags: bool,
|
||||
|
@ -546,22 +546,22 @@ pub struct FolderPermissions {
|
|||
pub change_permissions: bool,
|
||||
}
|
||||
|
||||
impl Default for FolderPermissions {
|
||||
impl Default for MailboxPermissions {
|
||||
fn default() -> Self {
|
||||
FolderPermissions {
|
||||
MailboxPermissions {
|
||||
create_messages: false,
|
||||
remove_messages: false,
|
||||
set_flags: false,
|
||||
create_child: false,
|
||||
rename_messages: false,
|
||||
delete_messages: false,
|
||||
delete_mailbox: false,
|
||||
delete_mailbox: true,
|
||||
change_permissions: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for FolderPermissions {
|
||||
impl std::fmt::Display for MailboxPermissions {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(fmt, "{:#?}", self)
|
||||
}
|
||||
|
|
|
@ -24,8 +24,8 @@ use smallvec::SmallVec;
|
|||
#[macro_use]
|
||||
mod protocol_parser;
|
||||
pub use protocol_parser::{UntaggedResponse::*, *};
|
||||
mod folder;
|
||||
pub use folder::*;
|
||||
mod mailbox;
|
||||
pub use mailbox::*;
|
||||
mod operations;
|
||||
pub use operations::*;
|
||||
mod connection;
|
||||
|
@ -35,10 +35,10 @@ pub use watch::*;
|
|||
|
||||
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
||||
use crate::backends::BackendOp;
|
||||
use crate::backends::FolderHash;
|
||||
use crate::backends::MailboxHash;
|
||||
use crate::backends::RefreshEvent;
|
||||
use crate::backends::RefreshEventKind::{self, *};
|
||||
use crate::backends::{BackendFolder, Folder, MailBackend, RefreshEventConsumer};
|
||||
use crate::backends::{BackendMailbox, MailBackend, Mailbox, RefreshEventConsumer};
|
||||
use crate::conf::AccountSettings;
|
||||
use crate::email::*;
|
||||
use crate::error::{MeliError, Result};
|
||||
|
@ -117,8 +117,8 @@ macro_rules! get_conf_val {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct UIDStore {
|
||||
uidvalidity: Arc<Mutex<FnvHashMap<FolderHash, UID>>>,
|
||||
hash_index: Arc<Mutex<FnvHashMap<EnvelopeHash, (UID, FolderHash)>>>,
|
||||
uidvalidity: Arc<Mutex<FnvHashMap<MailboxHash, UID>>>,
|
||||
hash_index: Arc<Mutex<FnvHashMap<EnvelopeHash, (UID, MailboxHash)>>>,
|
||||
uid_index: Arc<Mutex<FnvHashMap<UID, EnvelopeHash>>>,
|
||||
|
||||
byte_cache: Arc<Mutex<FnvHashMap<UID, EnvelopeCache>>>,
|
||||
|
@ -134,7 +134,7 @@ pub struct ImapType {
|
|||
can_create_flags: Arc<Mutex<bool>>,
|
||||
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||
|
||||
folders: Arc<RwLock<FnvHashMap<FolderHash, ImapFolder>>>,
|
||||
mailboxes: Arc<RwLock<FnvHashMap<MailboxHash, ImapMailbox>>>,
|
||||
}
|
||||
|
||||
impl MailBackend for ImapType {
|
||||
|
@ -152,16 +152,16 @@ impl MailBackend for ImapType {
|
|||
}
|
||||
}
|
||||
|
||||
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||
fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
|
||||
let mut w = AsyncBuilder::new();
|
||||
let handle = {
|
||||
let tx = w.tx();
|
||||
let uid_store = self.uid_store.clone();
|
||||
let tag_index = self.tag_index.clone();
|
||||
let can_create_flags = self.can_create_flags.clone();
|
||||
let folder_hash = folder.hash();
|
||||
let (permissions, folder_path, folder_exists, no_select, unseen) = {
|
||||
let f = &self.folders.read().unwrap()[&folder_hash];
|
||||
let mailbox_hash = mailbox.hash();
|
||||
let (permissions, mailbox_path, mailbox_exists, no_select, unseen) = {
|
||||
let f = &self.mailboxes.read().unwrap()[&mailbox_hash];
|
||||
(
|
||||
f.permissions.clone(),
|
||||
f.imap_path().to_string(),
|
||||
|
@ -182,24 +182,24 @@ impl MailBackend for ImapType {
|
|||
let tx = _tx;
|
||||
let mut response = String::with_capacity(8 * 1024);
|
||||
let mut conn = connection.lock()?;
|
||||
debug!("locked for get {}", folder_path);
|
||||
debug!("locked for get {}", mailbox_path);
|
||||
|
||||
/* first SELECT the mailbox to get READ/WRITE permissions (because EXAMINE only
|
||||
* returns READ-ONLY for both cases) */
|
||||
conn.send_command(format!("SELECT \"{}\"", folder_path).as_bytes())?;
|
||||
conn.send_command(format!("SELECT \"{}\"", mailbox_path).as_bytes())?;
|
||||
conn.read_response(&mut response)?;
|
||||
let examine_response = protocol_parser::select_response(&response)?;
|
||||
*can_create_flags.lock().unwrap() = examine_response.can_create_flags;
|
||||
debug!(
|
||||
"folder: {} examine_response: {:?}",
|
||||
folder_path, examine_response
|
||||
"mailbox: {} examine_response: {:?}",
|
||||
mailbox_path, examine_response
|
||||
);
|
||||
let mut exists: usize = examine_response.uidnext - 1;
|
||||
{
|
||||
let mut uidvalidities = uid_store.uidvalidity.lock().unwrap();
|
||||
|
||||
let v = uidvalidities
|
||||
.entry(folder_hash)
|
||||
.entry(mailbox_hash)
|
||||
.or_insert(examine_response.uidvalidity);
|
||||
*v = examine_response.uidvalidity;
|
||||
|
||||
|
@ -210,11 +210,11 @@ impl MailBackend for ImapType {
|
|||
permissions.rename_messages = !examine_response.read_only;
|
||||
permissions.delete_messages = !examine_response.read_only;
|
||||
permissions.delete_messages = !examine_response.read_only;
|
||||
let mut folder_exists = folder_exists.lock().unwrap();
|
||||
*folder_exists = exists;
|
||||
let mut mailbox_exists = mailbox_exists.lock().unwrap();
|
||||
*mailbox_exists = exists;
|
||||
}
|
||||
/* reselecting the same mailbox with EXAMINE prevents expunging it */
|
||||
conn.send_command(format!("EXAMINE \"{}\"", folder_path).as_bytes())?;
|
||||
conn.send_command(format!("EXAMINE \"{}\"", mailbox_path).as_bytes())?;
|
||||
conn.read_response(&mut response)?;
|
||||
|
||||
let mut tag_lck = tag_index.write().unwrap();
|
||||
|
@ -247,7 +247,7 @@ impl MailBackend for ImapType {
|
|||
let mut env = envelope.unwrap();
|
||||
let mut h = DefaultHasher::new();
|
||||
h.write_usize(uid);
|
||||
h.write(folder_path.as_bytes());
|
||||
h.write(mailbox_path.as_bytes());
|
||||
env.set_hash(h.finish());
|
||||
if let Some((flags, keywords)) = flags {
|
||||
if !flags.contains(Flag::SEEN) {
|
||||
|
@ -266,7 +266,7 @@ impl MailBackend for ImapType {
|
|||
.hash_index
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(env.hash(), (uid, folder_hash));
|
||||
.insert(env.hash(), (uid, mailbox_hash));
|
||||
uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
|
||||
envelopes.push(env);
|
||||
}
|
||||
|
@ -290,15 +290,15 @@ impl MailBackend for ImapType {
|
|||
|
||||
fn refresh(
|
||||
&mut self,
|
||||
folder_hash: FolderHash,
|
||||
mailbox_hash: MailboxHash,
|
||||
sender: RefreshEventConsumer,
|
||||
) -> Result<Async<()>> {
|
||||
self.connection.lock().unwrap().connect()?;
|
||||
let inbox = self
|
||||
.folders
|
||||
.mailboxes
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&folder_hash)
|
||||
.get(&mailbox_hash)
|
||||
.map(std::clone::Clone::clone)
|
||||
.unwrap();
|
||||
let tag_index = self.tag_index.clone();
|
||||
|
@ -340,7 +340,7 @@ impl MailBackend for ImapType {
|
|||
sender: RefreshEventConsumer,
|
||||
work_context: WorkContext,
|
||||
) -> Result<std::thread::ThreadId> {
|
||||
let folders = self.folders.clone();
|
||||
let mailboxes = self.mailboxes.clone();
|
||||
let tag_index = self.tag_index.clone();
|
||||
let conn = ImapConnection::new_connection(&self.server_conf, self.online.clone());
|
||||
let main_conn = self.connection.clone();
|
||||
|
@ -365,7 +365,7 @@ impl MailBackend for ImapType {
|
|||
is_online,
|
||||
main_conn,
|
||||
uid_store,
|
||||
folders,
|
||||
mailboxes,
|
||||
sender,
|
||||
work_context,
|
||||
tag_index,
|
||||
|
@ -379,38 +379,41 @@ impl MailBackend for ImapType {
|
|||
Ok(handle.thread().id())
|
||||
}
|
||||
|
||||
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
|
||||
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||
{
|
||||
let folders = self.folders.read().unwrap();
|
||||
if !folders.is_empty() {
|
||||
return Ok(folders
|
||||
let mailboxes = self.mailboxes.read().unwrap();
|
||||
if !mailboxes.is_empty() {
|
||||
return Ok(mailboxes
|
||||
.iter()
|
||||
.map(|(h, f)| (*h, Box::new(Clone::clone(f)) as Folder))
|
||||
.map(|(h, f)| (*h, Box::new(Clone::clone(f)) as Mailbox))
|
||||
.collect());
|
||||
}
|
||||
}
|
||||
let mut folders = self.folders.write()?;
|
||||
*folders = ImapType::imap_folders(&self.connection)?;
|
||||
folders.retain(|_, f| (self.is_subscribed)(f.path()));
|
||||
let keys = folders.keys().cloned().collect::<FnvHashSet<FolderHash>>();
|
||||
let mut mailboxes = self.mailboxes.write()?;
|
||||
*mailboxes = ImapType::imap_mailboxes(&self.connection)?;
|
||||
mailboxes.retain(|_, f| (self.is_subscribed)(f.path()));
|
||||
let keys = mailboxes
|
||||
.keys()
|
||||
.cloned()
|
||||
.collect::<FnvHashSet<MailboxHash>>();
|
||||
let mut uid_lock = self.uid_store.uidvalidity.lock().unwrap();
|
||||
for f in folders.values_mut() {
|
||||
for f in mailboxes.values_mut() {
|
||||
uid_lock.entry(f.hash()).or_default();
|
||||
f.children.retain(|c| keys.contains(c));
|
||||
}
|
||||
drop(uid_lock);
|
||||
Ok(folders
|
||||
Ok(mailboxes
|
||||
.iter()
|
||||
.filter(|(_, f)| f.is_subscribed)
|
||||
.map(|(h, f)| (*h, Box::new(Clone::clone(f)) as Folder))
|
||||
.map(|(h, f)| (*h, Box::new(Clone::clone(f)) as Mailbox))
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp> {
|
||||
let (uid, folder_hash) = self.uid_store.hash_index.lock().unwrap()[&hash];
|
||||
let (uid, mailbox_hash) = self.uid_store.hash_index.lock().unwrap()[&hash];
|
||||
Box::new(ImapOp::new(
|
||||
uid,
|
||||
self.folders.read().unwrap()[&folder_hash]
|
||||
self.mailboxes.read().unwrap()[&mailbox_hash]
|
||||
.imap_path()
|
||||
.to_string(),
|
||||
self.connection.clone(),
|
||||
|
@ -419,28 +422,28 @@ impl MailBackend for ImapType {
|
|||
))
|
||||
}
|
||||
|
||||
fn save(&self, bytes: &[u8], folder: &str, flags: Option<Flag>) -> Result<()> {
|
||||
fn save(&self, bytes: &[u8], mailbox: &str, flags: Option<Flag>) -> Result<()> {
|
||||
let path = {
|
||||
let folders = self.folders.read().unwrap();
|
||||
let mailboxes = self.mailboxes.read().unwrap();
|
||||
|
||||
let f_result = folders
|
||||
let f_result = mailboxes
|
||||
.values()
|
||||
.find(|v| v.path == folder || v.name == folder);
|
||||
.find(|v| v.path == mailbox || v.name == mailbox);
|
||||
if f_result
|
||||
.map(|f| !f.permissions.lock().unwrap().create_messages)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return Err(MeliError::new(format!(
|
||||
"You are not allowed to create messages in folder {}",
|
||||
folder
|
||||
"You are not allowed to create messages in mailbox {}",
|
||||
mailbox
|
||||
)));
|
||||
}
|
||||
|
||||
f_result
|
||||
.map(|v| v.imap_path().to_string())
|
||||
.ok_or(MeliError::new(format!(
|
||||
"Folder with name {} not found.",
|
||||
folder
|
||||
"Mailbox with name {} not found.",
|
||||
mailbox
|
||||
)))?
|
||||
};
|
||||
let mut response = String::with_capacity(8 * 1024);
|
||||
|
@ -478,10 +481,10 @@ impl MailBackend for ImapType {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_folder(
|
||||
fn create_mailbox(
|
||||
&mut self,
|
||||
mut path: String,
|
||||
) -> Result<(FolderHash, FnvHashMap<FolderHash, Folder>)> {
|
||||
) -> Result<(MailboxHash, FnvHashMap<MailboxHash, Mailbox>)> {
|
||||
/* Must transform path to something the IMAP server will accept
|
||||
*
|
||||
* Each root mailbox has a hierarchy delimeter reported by the LIST entry. All paths
|
||||
|
@ -496,21 +499,21 @@ impl MailBackend for ImapType {
|
|||
* decision is unpleasant for you.
|
||||
*/
|
||||
|
||||
let mut folders = self.folders.write().unwrap();
|
||||
for root_folder in folders.values().filter(|f| f.parent.is_none()) {
|
||||
if path.starts_with(&root_folder.name) {
|
||||
debug!("path starts with {:?}", &root_folder);
|
||||
let mut mailboxes = self.mailboxes.write().unwrap();
|
||||
for root_mailbox in mailboxes.values().filter(|f| f.parent.is_none()) {
|
||||
if path.starts_with(&root_mailbox.name) {
|
||||
debug!("path starts with {:?}", &root_mailbox);
|
||||
path = path.replace(
|
||||
'/',
|
||||
(root_folder.separator as char).encode_utf8(&mut [0; 4]),
|
||||
(root_mailbox.separator as char).encode_utf8(&mut [0; 4]),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if folders.values().any(|f| f.path == path) {
|
||||
if mailboxes.values().any(|f| f.path == path) {
|
||||
return Err(MeliError::new(format!(
|
||||
"Folder named `{}` in account `{}` already exists.",
|
||||
"Mailbox named `{}` in account `{}` already exists.",
|
||||
path, self.account_name,
|
||||
)));
|
||||
}
|
||||
|
@ -527,21 +530,24 @@ impl MailBackend for ImapType {
|
|||
let ret: Result<()> = ImapResponse::from(&response).into();
|
||||
ret?;
|
||||
let new_hash = get_path_hash!(path.as_str());
|
||||
folders.clear();
|
||||
drop(folders);
|
||||
Ok((new_hash, self.folders().map_err(|err| MeliError::new(format!("Mailbox create was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err)))?))
|
||||
mailboxes.clear();
|
||||
drop(mailboxes);
|
||||
Ok((new_hash, self.mailboxes().map_err(|err| MeliError::new(format!("Mailbox create was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err)))?))
|
||||
}
|
||||
|
||||
fn delete_folder(&mut self, folder_hash: FolderHash) -> Result<FnvHashMap<FolderHash, Folder>> {
|
||||
let mut folders = self.folders.write().unwrap();
|
||||
let permissions = folders[&folder_hash].permissions();
|
||||
fn delete_mailbox(
|
||||
&mut self,
|
||||
mailbox_hash: MailboxHash,
|
||||
) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||
let mut mailboxes = self.mailboxes.write().unwrap();
|
||||
let permissions = mailboxes[&mailbox_hash].permissions();
|
||||
if !permissions.delete_mailbox {
|
||||
return Err(MeliError::new(format!("You do not have permission to delete `{}`. Set permissions for this mailbox are {}", folders[&folder_hash].name(), permissions)));
|
||||
return Err(MeliError::new(format!("You do not have permission to delete `{}`. Set permissions for this mailbox are {}", mailboxes[&mailbox_hash].name(), permissions)));
|
||||
}
|
||||
let mut response = String::with_capacity(8 * 1024);
|
||||
{
|
||||
let mut conn_lck = self.connection.lock()?;
|
||||
if !folders[&folder_hash].no_select {
|
||||
if !mailboxes[&mailbox_hash].no_select {
|
||||
/* make sure mailbox is not selected before it gets deleted, otherwise
|
||||
* connection gets dropped by server */
|
||||
if conn_lck
|
||||
|
@ -550,42 +556,46 @@ impl MailBackend for ImapType {
|
|||
.any(|cap| cap.eq_ignore_ascii_case(b"UNSELECT"))
|
||||
{
|
||||
conn_lck.send_command(
|
||||
format!("UNSELECT \"{}\"", folders[&folder_hash].imap_path()).as_bytes(),
|
||||
format!("UNSELECT \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
|
||||
)?;
|
||||
conn_lck.read_response(&mut response)?;
|
||||
} else {
|
||||
conn_lck.send_command(
|
||||
format!("SELECT \"{}\"", folders[&folder_hash].imap_path()).as_bytes(),
|
||||
format!("SELECT \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
|
||||
)?;
|
||||
conn_lck.read_response(&mut response)?;
|
||||
conn_lck.send_command(
|
||||
format!("EXAMINE \"{}\"", folders[&folder_hash].imap_path()).as_bytes(),
|
||||
format!("EXAMINE \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
|
||||
)?;
|
||||
conn_lck.read_response(&mut response)?;
|
||||
}
|
||||
}
|
||||
if folders[&folder_hash].is_subscribed() {
|
||||
if mailboxes[&mailbox_hash].is_subscribed() {
|
||||
conn_lck.send_command(
|
||||
format!("UNSUBSCRIBE \"{}\"", folders[&folder_hash].imap_path()).as_bytes(),
|
||||
format!("UNSUBSCRIBE \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
|
||||
)?;
|
||||
conn_lck.read_response(&mut response)?;
|
||||
}
|
||||
|
||||
conn_lck.send_command(
|
||||
debug!(format!("DELETE \"{}\"", folders[&folder_hash].imap_path())).as_bytes(),
|
||||
debug!(format!(
|
||||
"DELETE \"{}\"",
|
||||
mailboxes[&mailbox_hash].imap_path()
|
||||
))
|
||||
.as_bytes(),
|
||||
)?;
|
||||
conn_lck.read_response(&mut response)?;
|
||||
}
|
||||
let ret: Result<()> = ImapResponse::from(&response).into();
|
||||
ret?;
|
||||
folders.clear();
|
||||
drop(folders);
|
||||
self.folders().map_err(|err| format!("Mailbox delete was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err).into())
|
||||
mailboxes.clear();
|
||||
drop(mailboxes);
|
||||
self.mailboxes().map_err(|err| format!("Mailbox delete was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err).into())
|
||||
}
|
||||
|
||||
fn set_folder_subscription(&mut self, folder_hash: FolderHash, new_val: bool) -> Result<()> {
|
||||
let mut folders = self.folders.write().unwrap();
|
||||
if folders[&folder_hash].is_subscribed() == new_val {
|
||||
fn set_mailbox_subscription(&mut self, mailbox_hash: MailboxHash, new_val: bool) -> Result<()> {
|
||||
let mut mailboxes = self.mailboxes.write().unwrap();
|
||||
if mailboxes[&mailbox_hash].is_subscribed() == new_val {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -594,11 +604,11 @@ impl MailBackend for ImapType {
|
|||
let mut conn_lck = self.connection.lock()?;
|
||||
if new_val {
|
||||
conn_lck.send_command(
|
||||
format!("SUBSCRIBE \"{}\"", folders[&folder_hash].imap_path()).as_bytes(),
|
||||
format!("SUBSCRIBE \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
|
||||
)?;
|
||||
} else {
|
||||
conn_lck.send_command(
|
||||
format!("UNSUBSCRIBE \"{}\"", folders[&folder_hash].imap_path()).as_bytes(),
|
||||
format!("UNSUBSCRIBE \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
|
||||
)?;
|
||||
}
|
||||
conn_lck.read_response(&mut response)?;
|
||||
|
@ -606,24 +616,28 @@ impl MailBackend for ImapType {
|
|||
|
||||
let ret: Result<()> = ImapResponse::from(&response).into();
|
||||
if ret.is_ok() {
|
||||
folders.entry(folder_hash).and_modify(|entry| {
|
||||
mailboxes.entry(mailbox_hash).and_modify(|entry| {
|
||||
let _ = entry.set_is_subscribed(new_val);
|
||||
});
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn rename_folder(&mut self, folder_hash: FolderHash, mut new_path: String) -> Result<Folder> {
|
||||
let mut folders = self.folders.write().unwrap();
|
||||
let permissions = folders[&folder_hash].permissions();
|
||||
fn rename_mailbox(
|
||||
&mut self,
|
||||
mailbox_hash: MailboxHash,
|
||||
mut new_path: String,
|
||||
) -> Result<Mailbox> {
|
||||
let mut mailboxes = self.mailboxes.write().unwrap();
|
||||
let permissions = mailboxes[&mailbox_hash].permissions();
|
||||
if !permissions.delete_mailbox {
|
||||
return Err(MeliError::new(format!("You do not have permission to rename folder `{}` (rename is equivalent to delete + create). Set permissions for this mailbox are {}", folders[&folder_hash].name(), permissions)));
|
||||
return Err(MeliError::new(format!("You do not have permission to rename mailbox `{}` (rename is equivalent to delete + create). Set permissions for this mailbox are {}", mailboxes[&mailbox_hash].name(), permissions)));
|
||||
}
|
||||
let mut response = String::with_capacity(8 * 1024);
|
||||
if folders[&folder_hash].separator != b'/' {
|
||||
if mailboxes[&mailbox_hash].separator != b'/' {
|
||||
new_path = new_path.replace(
|
||||
'/',
|
||||
(folders[&folder_hash].separator as char).encode_utf8(&mut [0; 4]),
|
||||
(mailboxes[&mailbox_hash].separator as char).encode_utf8(&mut [0; 4]),
|
||||
);
|
||||
}
|
||||
{
|
||||
|
@ -631,7 +645,7 @@ impl MailBackend for ImapType {
|
|||
conn_lck.send_command(
|
||||
debug!(format!(
|
||||
"RENAME \"{}\" \"{}\"",
|
||||
folders[&folder_hash].imap_path(),
|
||||
mailboxes[&mailbox_hash].imap_path(),
|
||||
new_path
|
||||
))
|
||||
.as_bytes(),
|
||||
|
@ -641,23 +655,23 @@ impl MailBackend for ImapType {
|
|||
let new_hash = get_path_hash!(new_path.as_str());
|
||||
let ret: Result<()> = ImapResponse::from(&response).into();
|
||||
ret?;
|
||||
folders.clear();
|
||||
drop(folders);
|
||||
self.folders().map_err(|err| format!("Mailbox rename was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err))?;
|
||||
Ok(BackendFolder::clone(
|
||||
&self.folders.read().unwrap()[&new_hash],
|
||||
mailboxes.clear();
|
||||
drop(mailboxes);
|
||||
self.mailboxes().map_err(|err| format!("Mailbox rename was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err))?;
|
||||
Ok(BackendMailbox::clone(
|
||||
&self.mailboxes.read().unwrap()[&new_hash],
|
||||
))
|
||||
}
|
||||
|
||||
fn set_folder_permissions(
|
||||
fn set_mailbox_permissions(
|
||||
&mut self,
|
||||
folder_hash: FolderHash,
|
||||
_val: crate::backends::FolderPermissions,
|
||||
mailbox_hash: MailboxHash,
|
||||
_val: crate::backends::MailboxPermissions,
|
||||
) -> Result<()> {
|
||||
let folders = self.folders.write().unwrap();
|
||||
let permissions = folders[&folder_hash].permissions();
|
||||
let mailboxes = self.mailboxes.write().unwrap();
|
||||
let permissions = mailboxes[&mailbox_hash].permissions();
|
||||
if !permissions.change_permissions {
|
||||
return Err(MeliError::new(format!("You do not have permission to change permissions for folder `{}`. Set permissions for this mailbox are {}", folders[&folder_hash].name(), permissions)));
|
||||
return Err(MeliError::new(format!("You do not have permission to change permissions for mailbox `{}`. Set permissions for this mailbox are {}", mailboxes[&mailbox_hash].name(), permissions)));
|
||||
}
|
||||
|
||||
Err(MeliError::new("Unimplemented."))
|
||||
|
@ -698,7 +712,7 @@ impl ImapType {
|
|||
|
||||
can_create_flags: Arc::new(Mutex::new(false)),
|
||||
tag_index: Arc::new(RwLock::new(Default::default())),
|
||||
folders: Arc::new(RwLock::new(Default::default())),
|
||||
mailboxes: Arc::new(RwLock::new(Default::default())),
|
||||
connection: Arc::new(Mutex::new(connection)),
|
||||
uid_store: Arc::new(UIDStore {
|
||||
uidvalidity: Default::default(),
|
||||
|
@ -742,10 +756,10 @@ impl ImapType {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn imap_folders(
|
||||
pub fn imap_mailboxes(
|
||||
connection: &Arc<Mutex<ImapConnection>>,
|
||||
) -> Result<FnvHashMap<FolderHash, ImapFolder>> {
|
||||
let mut folders: FnvHashMap<FolderHash, ImapFolder> = Default::default();
|
||||
) -> Result<FnvHashMap<MailboxHash, ImapMailbox>> {
|
||||
let mut mailboxes: FnvHashMap<MailboxHash, ImapMailbox> = Default::default();
|
||||
let mut res = String::with_capacity(8 * 1024);
|
||||
let mut conn = connection.lock().unwrap();
|
||||
conn.send_command(b"LIST \"\" \"*\"")?;
|
||||
|
@ -755,33 +769,33 @@ impl ImapType {
|
|||
/* Remove "M__ OK .." line */
|
||||
lines.next_back();
|
||||
for l in lines.map(|l| l.trim()) {
|
||||
if let Ok(mut folder) =
|
||||
protocol_parser::list_folder_result(l.as_bytes()).to_full_result()
|
||||
if let Ok(mut mailbox) =
|
||||
protocol_parser::list_mailbox_result(l.as_bytes()).to_full_result()
|
||||
{
|
||||
if let Some(parent) = folder.parent {
|
||||
if folders.contains_key(&parent) {
|
||||
folders
|
||||
if let Some(parent) = mailbox.parent {
|
||||
if mailboxes.contains_key(&parent) {
|
||||
mailboxes
|
||||
.entry(parent)
|
||||
.and_modify(|e| e.children.push(folder.hash));
|
||||
.and_modify(|e| e.children.push(mailbox.hash));
|
||||
} else {
|
||||
/* Insert dummy parent entry, populating only the children field. Later
|
||||
* when we encounter the parent entry we will swap its children with
|
||||
* dummy's */
|
||||
folders.insert(
|
||||
mailboxes.insert(
|
||||
parent,
|
||||
ImapFolder {
|
||||
children: vec![folder.hash],
|
||||
..ImapFolder::default()
|
||||
ImapMailbox {
|
||||
children: vec![mailbox.hash],
|
||||
..ImapMailbox::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
if folders.contains_key(&folder.hash) {
|
||||
let entry = folders.entry(folder.hash).or_default();
|
||||
std::mem::swap(&mut entry.children, &mut folder.children);
|
||||
*entry = folder;
|
||||
if mailboxes.contains_key(&mailbox.hash) {
|
||||
let entry = mailboxes.entry(mailbox.hash).or_default();
|
||||
std::mem::swap(&mut entry.children, &mut mailbox.children);
|
||||
*entry = mailbox;
|
||||
} else {
|
||||
folders.insert(folder.hash, folder);
|
||||
mailboxes.insert(mailbox.hash, mailbox);
|
||||
}
|
||||
} else {
|
||||
debug!("parse error for {:?}", l);
|
||||
|
@ -795,9 +809,9 @@ impl ImapType {
|
|||
lines.next_back();
|
||||
for l in lines.map(|l| l.trim()) {
|
||||
if let Ok(subscription) =
|
||||
protocol_parser::list_folder_result(l.as_bytes()).to_full_result()
|
||||
protocol_parser::list_mailbox_result(l.as_bytes()).to_full_result()
|
||||
{
|
||||
if let Some(f) = folders.get_mut(&subscription.hash()) {
|
||||
if let Some(f) = mailboxes.get_mut(&subscription.hash()) {
|
||||
if subscription.no_select {
|
||||
continue;
|
||||
}
|
||||
|
@ -807,7 +821,7 @@ impl ImapType {
|
|||
debug!("parse error for {:?}", l);
|
||||
}
|
||||
}
|
||||
Ok(debug!(folders))
|
||||
Ok(debug!(mailboxes))
|
||||
}
|
||||
|
||||
pub fn capabilities(&self) -> Vec<String> {
|
||||
|
@ -823,13 +837,13 @@ impl ImapType {
|
|||
pub fn search(
|
||||
&self,
|
||||
query: String,
|
||||
folder_hash: FolderHash,
|
||||
mailbox_hash: MailboxHash,
|
||||
) -> Result<SmallVec<[EnvelopeHash; 512]>> {
|
||||
let folders_lck = self.folders.read()?;
|
||||
let mailboxes_lck = self.mailboxes.read()?;
|
||||
let mut response = String::with_capacity(8 * 1024);
|
||||
let mut conn = self.connection.lock()?;
|
||||
conn.send_command(
|
||||
format!("EXAMINE \"{}\"", folders_lck[&folder_hash].imap_path()).as_bytes(),
|
||||
format!("EXAMINE \"{}\"", mailboxes_lck[&mailbox_hash].imap_path()).as_bytes(),
|
||||
)?;
|
||||
conn.read_response(&mut response)?;
|
||||
conn.send_command(format!("UID SEARCH CHARSET UTF-8 {}", query).as_bytes())?;
|
||||
|
|
|
@ -18,36 +18,38 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use crate::backends::{BackendFolder, Folder, FolderHash, FolderPermissions, SpecialUsageMailbox};
|
||||
use crate::backends::{
|
||||
BackendMailbox, Mailbox, MailboxHash, MailboxPermissions, SpecialUsageMailbox,
|
||||
};
|
||||
use crate::error::*;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct ImapFolder {
|
||||
pub(super) hash: FolderHash,
|
||||
pub struct ImapMailbox {
|
||||
pub(super) hash: MailboxHash,
|
||||
pub(super) imap_path: String,
|
||||
pub(super) path: String,
|
||||
pub(super) name: String,
|
||||
pub(super) parent: Option<FolderHash>,
|
||||
pub(super) children: Vec<FolderHash>,
|
||||
pub(super) parent: Option<MailboxHash>,
|
||||
pub(super) children: Vec<MailboxHash>,
|
||||
pub separator: u8,
|
||||
pub usage: Arc<RwLock<SpecialUsageMailbox>>,
|
||||
pub no_select: bool,
|
||||
pub is_subscribed: bool,
|
||||
|
||||
pub permissions: Arc<Mutex<FolderPermissions>>,
|
||||
pub permissions: Arc<Mutex<MailboxPermissions>>,
|
||||
pub exists: Arc<Mutex<usize>>,
|
||||
pub unseen: Arc<Mutex<usize>>,
|
||||
}
|
||||
|
||||
impl ImapFolder {
|
||||
impl ImapMailbox {
|
||||
pub fn imap_path(&self) -> &str {
|
||||
&self.imap_path
|
||||
}
|
||||
}
|
||||
|
||||
impl BackendFolder for ImapFolder {
|
||||
fn hash(&self) -> FolderHash {
|
||||
impl BackendMailbox for ImapMailbox {
|
||||
fn hash(&self) -> MailboxHash {
|
||||
self.hash
|
||||
}
|
||||
|
||||
|
@ -63,11 +65,11 @@ impl BackendFolder for ImapFolder {
|
|||
self.name = s.to_string();
|
||||
}
|
||||
|
||||
fn children(&self) -> &[FolderHash] {
|
||||
fn children(&self) -> &[MailboxHash] {
|
||||
&self.children
|
||||
}
|
||||
|
||||
fn clone(&self) -> Folder {
|
||||
fn clone(&self) -> Mailbox {
|
||||
Box::new(std::clone::Clone::clone(self))
|
||||
}
|
||||
|
||||
|
@ -75,11 +77,11 @@ impl BackendFolder for ImapFolder {
|
|||
*self.usage.read().unwrap()
|
||||
}
|
||||
|
||||
fn parent(&self) -> Option<FolderHash> {
|
||||
fn parent(&self) -> Option<MailboxHash> {
|
||||
self.parent
|
||||
}
|
||||
|
||||
fn permissions(&self) -> FolderPermissions {
|
||||
fn permissions(&self) -> MailboxPermissions {
|
||||
*self.permissions.lock().unwrap()
|
||||
}
|
||||
fn is_subscribed(&self) -> bool {
|
|
@ -34,7 +34,7 @@ pub struct ImapOp {
|
|||
bytes: Option<String>,
|
||||
headers: Option<String>,
|
||||
body: Option<String>,
|
||||
folder_path: String,
|
||||
mailbox_path: String,
|
||||
flags: Cell<Option<Flag>>,
|
||||
connection: Arc<Mutex<ImapConnection>>,
|
||||
uid_store: Arc<UIDStore>,
|
||||
|
@ -44,7 +44,7 @@ pub struct ImapOp {
|
|||
impl ImapOp {
|
||||
pub fn new(
|
||||
uid: usize,
|
||||
folder_path: String,
|
||||
mailbox_path: String,
|
||||
connection: Arc<Mutex<ImapConnection>>,
|
||||
uid_store: Arc<UIDStore>,
|
||||
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||
|
@ -55,7 +55,7 @@ impl ImapOp {
|
|||
bytes: None,
|
||||
headers: None,
|
||||
body: None,
|
||||
folder_path,
|
||||
mailbox_path,
|
||||
flags: Cell::new(None),
|
||||
uid_store,
|
||||
tag_index,
|
||||
|
@ -78,7 +78,7 @@ impl BackendOp for ImapOp {
|
|||
let mut response = String::with_capacity(8 * 1024);
|
||||
{
|
||||
let mut conn = self.connection.lock().unwrap();
|
||||
conn.send_command(format!("SELECT \"{}\"", &self.folder_path,).as_bytes())?;
|
||||
conn.send_command(format!("SELECT \"{}\"", &self.mailbox_path,).as_bytes())?;
|
||||
conn.read_response(&mut response)?;
|
||||
conn.send_command(format!("UID FETCH {} (FLAGS RFC822)", self.uid).as_bytes())?;
|
||||
conn.read_response(&mut response)?;
|
||||
|
@ -116,7 +116,7 @@ impl BackendOp for ImapOp {
|
|||
} else {
|
||||
let mut response = String::with_capacity(8 * 1024);
|
||||
let mut conn = self.connection.lock().unwrap();
|
||||
conn.send_command(format!("EXAMINE \"{}\"", &self.folder_path,).as_bytes())
|
||||
conn.send_command(format!("EXAMINE \"{}\"", &self.mailbox_path,).as_bytes())
|
||||
.unwrap();
|
||||
conn.read_response(&mut response).unwrap();
|
||||
conn.send_command(format!("UID FETCH {} FLAGS", self.uid).as_bytes())
|
||||
|
@ -154,7 +154,7 @@ impl BackendOp for ImapOp {
|
|||
|
||||
let mut response = String::with_capacity(8 * 1024);
|
||||
let mut conn = self.connection.lock().unwrap();
|
||||
conn.send_command(format!("SELECT \"{}\"", &self.folder_path,).as_bytes())?;
|
||||
conn.send_command(format!("SELECT \"{}\"", &self.mailbox_path,).as_bytes())?;
|
||||
conn.read_response(&mut response)?;
|
||||
debug!(&response);
|
||||
conn.send_command(
|
||||
|
@ -190,7 +190,7 @@ impl BackendOp for ImapOp {
|
|||
fn set_tag(&mut self, envelope: &mut Envelope, tag: String, value: bool) -> Result<()> {
|
||||
let mut response = String::with_capacity(8 * 1024);
|
||||
let mut conn = self.connection.lock().unwrap();
|
||||
conn.send_command(format!("SELECT \"{}\"", &self.folder_path,).as_bytes())?;
|
||||
conn.send_command(format!("SELECT \"{}\"", &self.mailbox_path,).as_bytes())?;
|
||||
conn.read_response(&mut response)?;
|
||||
conn.send_command(
|
||||
format!(
|
||||
|
|
|
@ -213,7 +213,7 @@ macro_rules! dbg_dmp (
|
|||
* LIST (\HasChildren) "." INBOX
|
||||
*/
|
||||
named!(
|
||||
pub list_folder_result<ImapFolder>,
|
||||
pub list_mailbox_result<ImapMailbox>,
|
||||
do_parse!(
|
||||
ws!(alt_complete!(tag!("* LIST (") | tag!("* LSUB (")))
|
||||
>> properties: take_until!(&b")"[0..])
|
||||
|
@ -223,7 +223,7 @@ named!(
|
|||
>> path: alt_complete!(delimited!(tag!("\""), is_not!("\""), tag!("\"")) | call!(rest))
|
||||
>> ({
|
||||
let separator: u8 = separator[0];
|
||||
let mut f = ImapFolder::default();
|
||||
let mut f = ImapMailbox::default();
|
||||
f.no_select = false;
|
||||
f.is_subscribed = false;
|
||||
for p in properties.split(|&b| b == b' ') {
|
||||
|
|
|
@ -28,20 +28,20 @@ pub struct ImapWatchKit {
|
|||
pub is_online: Arc<Mutex<(Instant, Result<()>)>>,
|
||||
pub main_conn: Arc<Mutex<ImapConnection>>,
|
||||
pub uid_store: Arc<UIDStore>,
|
||||
pub folders: Arc<RwLock<FnvHashMap<FolderHash, ImapFolder>>>,
|
||||
pub mailboxes: Arc<RwLock<FnvHashMap<MailboxHash, ImapMailbox>>>,
|
||||
pub sender: RefreshEventConsumer,
|
||||
pub work_context: WorkContext,
|
||||
pub tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||
}
|
||||
|
||||
macro_rules! exit_on_error {
|
||||
($sender:expr, $folder_hash:ident, $work_context:ident, $thread_id:ident, $($result:expr)+) => {
|
||||
($sender:expr, $mailbox_hash:ident, $work_context:ident, $thread_id:ident, $($result:expr)+) => {
|
||||
$(if let Err(e) = $result {
|
||||
debug!("failure: {}", e.to_string());
|
||||
$work_context.set_status.send(($thread_id, e.to_string())).unwrap();
|
||||
$work_context.finished.send($thread_id).unwrap();
|
||||
$sender.send(RefreshEvent {
|
||||
hash: $folder_hash,
|
||||
hash: $mailbox_hash,
|
||||
kind: RefreshEventKind::Failure(e.clone()),
|
||||
});
|
||||
Err(e)
|
||||
|
@ -56,7 +56,7 @@ pub fn poll_with_examine(kit: ImapWatchKit) -> Result<()> {
|
|||
mut conn,
|
||||
main_conn,
|
||||
uid_store,
|
||||
folders,
|
||||
mailboxes,
|
||||
sender,
|
||||
work_context,
|
||||
tag_index,
|
||||
|
@ -76,17 +76,17 @@ pub fn poll_with_examine(kit: ImapWatchKit) -> Result<()> {
|
|||
.send((thread_id, "sleeping...".to_string()))
|
||||
.unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_millis(5 * 60 * 1000));
|
||||
let folders = folders.read().unwrap();
|
||||
for folder in folders.values() {
|
||||
let mailboxes = mailboxes.read().unwrap();
|
||||
for mailbox in mailboxes.values() {
|
||||
work_context
|
||||
.set_status
|
||||
.send((
|
||||
thread_id,
|
||||
format!("examining `{}` for updates...", folder.path()),
|
||||
format!("examining `{}` for updates...", mailbox.path()),
|
||||
))
|
||||
.unwrap();
|
||||
examine_updates(
|
||||
folder,
|
||||
mailbox,
|
||||
&sender,
|
||||
&mut conn,
|
||||
&uid_store,
|
||||
|
@ -109,7 +109,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
is_online,
|
||||
main_conn,
|
||||
uid_store,
|
||||
folders,
|
||||
mailboxes,
|
||||
sender,
|
||||
work_context,
|
||||
tag_index,
|
||||
|
@ -122,16 +122,16 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
}
|
||||
conn.connect()?;
|
||||
let thread_id: std::thread::ThreadId = std::thread::current().id();
|
||||
let folder: ImapFolder = match folders
|
||||
let mailbox: ImapMailbox = match mailboxes
|
||||
.read()
|
||||
.unwrap()
|
||||
.values()
|
||||
.find(|f| f.parent.is_none() && (f.special_usage() == SpecialUsageMailbox::Inbox))
|
||||
.map(std::clone::Clone::clone)
|
||||
{
|
||||
Some(folder) => folder,
|
||||
Some(mailbox) => mailbox,
|
||||
None => {
|
||||
let err = MeliError::new("INBOX mailbox not found in local folder index. meli may have not parsed the IMAP folders correctly");
|
||||
let err = MeliError::new("INBOX mailbox not found in local mailbox index. meli may have not parsed the IMAP mailboxes correctly");
|
||||
debug!("failure: {}", err.to_string());
|
||||
work_context
|
||||
.set_status
|
||||
|
@ -144,28 +144,28 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
return Err(err);
|
||||
}
|
||||
};
|
||||
let folder_hash = folder.hash();
|
||||
let mailbox_hash = mailbox.hash();
|
||||
let mut response = String::with_capacity(8 * 1024);
|
||||
exit_on_error!(
|
||||
sender,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
work_context,
|
||||
thread_id,
|
||||
conn.send_command(format!("SELECT \"{}\"", folder.imap_path()).as_bytes())
|
||||
conn.send_command(format!("SELECT \"{}\"", mailbox.imap_path()).as_bytes())
|
||||
conn.read_response(&mut response)
|
||||
);
|
||||
debug!("select response {}", &response);
|
||||
{
|
||||
let mut prev_exists = folder.exists.lock().unwrap();
|
||||
let mut prev_exists = mailbox.exists.lock().unwrap();
|
||||
*prev_exists = match protocol_parser::select_response(&response) {
|
||||
Ok(ok) => {
|
||||
{
|
||||
let mut uidvalidities = uid_store.uidvalidity.lock().unwrap();
|
||||
|
||||
if let Some(v) = uidvalidities.get_mut(&folder_hash) {
|
||||
if let Some(v) = uidvalidities.get_mut(&mailbox_hash) {
|
||||
if *v != ok.uidvalidity {
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: RefreshEventKind::Rescan,
|
||||
});
|
||||
*prev_exists = 0;
|
||||
|
@ -176,15 +176,15 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
}
|
||||
} else {
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: RefreshEventKind::Rescan,
|
||||
});
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
||||
"Unknown mailbox: {} {}",
|
||||
folder.path(),
|
||||
folder_hash
|
||||
mailbox.path(),
|
||||
mailbox_hash
|
||||
))),
|
||||
});
|
||||
}
|
||||
|
@ -200,7 +200,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
}
|
||||
exit_on_error!(
|
||||
sender,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
work_context,
|
||||
thread_id,
|
||||
conn.send_command(b"IDLE")
|
||||
|
@ -214,14 +214,14 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
let mut watch = std::time::Instant::now();
|
||||
/* duration interval to send heartbeat */
|
||||
let _26_mins = std::time::Duration::from_secs(26 * 60);
|
||||
/* duration interval to check other folders for changes */
|
||||
/* duration interval to check other mailboxes for changes */
|
||||
let _5_mins = std::time::Duration::from_secs(5 * 60);
|
||||
while let Some(line) = iter.next() {
|
||||
let now = std::time::Instant::now();
|
||||
if now.duration_since(beat) >= _26_mins {
|
||||
exit_on_error!(
|
||||
sender,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
work_context,
|
||||
thread_id,
|
||||
iter.conn.set_nonblocking(true)
|
||||
|
@ -237,21 +237,21 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
if now.duration_since(watch) >= _5_mins {
|
||||
/* Time to poll all inboxes */
|
||||
let mut conn = main_conn.lock().unwrap();
|
||||
for folder in folders.read().unwrap().values() {
|
||||
for mailbox in mailboxes.read().unwrap().values() {
|
||||
work_context
|
||||
.set_status
|
||||
.send((
|
||||
thread_id,
|
||||
format!("examining `{}` for updates...", folder.path()),
|
||||
format!("examining `{}` for updates...", mailbox.path()),
|
||||
))
|
||||
.unwrap();
|
||||
exit_on_error!(
|
||||
sender,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
work_context,
|
||||
thread_id,
|
||||
examine_updates(
|
||||
folder,
|
||||
mailbox,
|
||||
&sender,
|
||||
&mut conn,
|
||||
&uid_store,
|
||||
|
@ -279,7 +279,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
/* UID SEARCH RECENT */
|
||||
exit_on_error!(
|
||||
sender,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
work_context,
|
||||
thread_id,
|
||||
conn.send_command(b"EXAMINE INBOX")
|
||||
|
@ -297,7 +297,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
Ok(v) => {
|
||||
exit_on_error!(
|
||||
sender,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
work_context,
|
||||
thread_id,
|
||||
conn.send_command(
|
||||
|
@ -331,13 +331,13 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
.hash_index
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(env.hash(), (uid, folder_hash));
|
||||
.insert(env.hash(), (uid, mailbox_hash));
|
||||
uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
|
||||
debug!(
|
||||
"Create event {} {} {}",
|
||||
env.hash(),
|
||||
env.subject(),
|
||||
folder.path(),
|
||||
mailbox.path(),
|
||||
);
|
||||
if let Some((_, keywords)) = flags {
|
||||
let mut tag_lck = tag_index.write().unwrap();
|
||||
|
@ -350,12 +350,12 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
}
|
||||
}
|
||||
if !env.is_seen() {
|
||||
*folder.unseen.lock().unwrap() += 1;
|
||||
*mailbox.unseen.lock().unwrap() += 1;
|
||||
}
|
||||
*folder.exists.lock().unwrap() += 1;
|
||||
*mailbox.exists.lock().unwrap() += 1;
|
||||
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: Create(Box::new(env)),
|
||||
});
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
let mut conn = main_conn.lock().unwrap();
|
||||
/* UID FETCH ALL UID, cross-ref, then FETCH difference headers
|
||||
* */
|
||||
let mut prev_exists = folder.exists.lock().unwrap();
|
||||
let mut prev_exists = mailbox.exists.lock().unwrap();
|
||||
debug!("exists {}", n);
|
||||
work_context
|
||||
.set_status
|
||||
|
@ -400,14 +400,14 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
"got `{} EXISTS` notification (EXISTS was previously {} for {}",
|
||||
n,
|
||||
*prev_exists,
|
||||
folder.path()
|
||||
mailbox.path()
|
||||
),
|
||||
))
|
||||
.unwrap();
|
||||
if n > *prev_exists {
|
||||
exit_on_error!(
|
||||
sender,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
work_context,
|
||||
thread_id,
|
||||
conn.send_command(b"EXAMINE INBOX")
|
||||
|
@ -450,7 +450,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
.hash_index
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(env.hash(), (uid, folder_hash));
|
||||
.insert(env.hash(), (uid, mailbox_hash));
|
||||
uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
|
||||
if let Some((_, keywords)) = flags {
|
||||
let mut tag_lck = tag_index.write().unwrap();
|
||||
|
@ -466,13 +466,13 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
"Create event {} {} {}",
|
||||
env.hash(),
|
||||
env.subject(),
|
||||
folder.path(),
|
||||
mailbox.path(),
|
||||
);
|
||||
if !env.is_seen() {
|
||||
*folder.unseen.lock().unwrap() += 1;
|
||||
*mailbox.unseen.lock().unwrap() += 1;
|
||||
}
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: Create(Box::new(env)),
|
||||
});
|
||||
}
|
||||
|
@ -507,7 +507,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
.unwrap();
|
||||
work_context.finished.send(thread_id).unwrap();
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
||||
"IDLE connection dropped: {}",
|
||||
&err
|
||||
|
@ -517,7 +517,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
|||
}
|
||||
|
||||
pub fn examine_updates(
|
||||
folder: &ImapFolder,
|
||||
mailbox: &ImapMailbox,
|
||||
sender: &RefreshEventConsumer,
|
||||
conn: &mut ImapConnection,
|
||||
uid_store: &Arc<UIDStore>,
|
||||
|
@ -525,15 +525,15 @@ pub fn examine_updates(
|
|||
tag_index: &Arc<RwLock<BTreeMap<u64, String>>>,
|
||||
) -> Result<()> {
|
||||
let thread_id: std::thread::ThreadId = std::thread::current().id();
|
||||
let folder_hash = folder.hash();
|
||||
debug!("examining folder {} {}", folder_hash, folder.path());
|
||||
let mailbox_hash = mailbox.hash();
|
||||
debug!("examining mailbox {} {}", mailbox_hash, mailbox.path());
|
||||
let mut response = String::with_capacity(8 * 1024);
|
||||
exit_on_error!(
|
||||
sender,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
work_context,
|
||||
thread_id,
|
||||
conn.send_command(format!("EXAMINE \"{}\"", folder.imap_path()).as_bytes())
|
||||
conn.send_command(format!("EXAMINE \"{}\"", mailbox.imap_path()).as_bytes())
|
||||
conn.read_response(&mut response)
|
||||
);
|
||||
match protocol_parser::select_response(&response) {
|
||||
|
@ -542,10 +542,10 @@ pub fn examine_updates(
|
|||
{
|
||||
let mut uidvalidities = uid_store.uidvalidity.lock().unwrap();
|
||||
|
||||
if let Some(v) = uidvalidities.get_mut(&folder_hash) {
|
||||
if let Some(v) = uidvalidities.get_mut(&mailbox_hash) {
|
||||
if *v != ok.uidvalidity {
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: RefreshEventKind::Rescan,
|
||||
});
|
||||
uid_store.uid_index.lock().unwrap().clear();
|
||||
|
@ -555,27 +555,27 @@ pub fn examine_updates(
|
|||
}
|
||||
} else {
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: RefreshEventKind::Rescan,
|
||||
});
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
||||
"Unknown mailbox: {} {}",
|
||||
folder.path(),
|
||||
folder_hash
|
||||
mailbox.path(),
|
||||
mailbox_hash
|
||||
))),
|
||||
});
|
||||
}
|
||||
}
|
||||
let mut prev_exists = folder.exists.lock().unwrap();
|
||||
let mut prev_exists = mailbox.exists.lock().unwrap();
|
||||
let n = ok.exists;
|
||||
if ok.recent > 0 {
|
||||
{
|
||||
/* UID SEARCH RECENT */
|
||||
exit_on_error!(
|
||||
sender,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
work_context,
|
||||
thread_id,
|
||||
conn.send_command(b"UID SEARCH RECENT")
|
||||
|
@ -591,7 +591,7 @@ pub fn examine_updates(
|
|||
Ok(v) => {
|
||||
exit_on_error!(
|
||||
sender,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
work_context,
|
||||
thread_id,
|
||||
conn.send_command(
|
||||
|
@ -615,7 +615,7 @@ pub fn examine_updates(
|
|||
.hash_index
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(env.hash(), (uid, folder_hash));
|
||||
.insert(env.hash(), (uid, mailbox_hash));
|
||||
uid_store
|
||||
.uid_index
|
||||
.lock()
|
||||
|
@ -625,7 +625,7 @@ pub fn examine_updates(
|
|||
"Create event {} {} {}",
|
||||
env.hash(),
|
||||
env.subject(),
|
||||
folder.path(),
|
||||
mailbox.path(),
|
||||
);
|
||||
if let Some((_, keywords)) = flags {
|
||||
let mut tag_lck = tag_index.write().unwrap();
|
||||
|
@ -638,10 +638,10 @@ pub fn examine_updates(
|
|||
}
|
||||
}
|
||||
if !env.is_seen() {
|
||||
*folder.unseen.lock().unwrap() += 1;
|
||||
*mailbox.unseen.lock().unwrap() += 1;
|
||||
}
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: Create(Box::new(env)),
|
||||
});
|
||||
}
|
||||
|
@ -667,7 +667,7 @@ pub fn examine_updates(
|
|||
debug!("exists {}", n);
|
||||
exit_on_error!(
|
||||
sender,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
work_context,
|
||||
thread_id,
|
||||
conn.send_command(
|
||||
|
@ -693,7 +693,7 @@ pub fn examine_updates(
|
|||
.hash_index
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(env.hash(), (uid, folder_hash));
|
||||
.insert(env.hash(), (uid, mailbox_hash));
|
||||
uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
|
||||
if let Some((_, keywords)) = flags {
|
||||
let mut tag_lck = tag_index.write().unwrap();
|
||||
|
@ -709,13 +709,13 @@ pub fn examine_updates(
|
|||
"Create event {} {} {}",
|
||||
env.hash(),
|
||||
env.subject(),
|
||||
folder.path(),
|
||||
mailbox.path(),
|
||||
);
|
||||
if !env.is_seen() {
|
||||
*folder.unseen.lock().unwrap() += 1;
|
||||
*mailbox.unseen.lock().unwrap() += 1;
|
||||
}
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: Create(Box::new(env)),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
|
||||
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
||||
use crate::backends::BackendOp;
|
||||
use crate::backends::FolderHash;
|
||||
use crate::backends::{BackendFolder, Folder, MailBackend, RefreshEventConsumer};
|
||||
use crate::backends::MailboxHash;
|
||||
use crate::backends::{BackendMailbox, MailBackend, Mailbox, RefreshEventConsumer};
|
||||
use crate::conf::AccountSettings;
|
||||
use crate::email::*;
|
||||
use crate::error::{MeliError, Result};
|
||||
|
@ -71,8 +71,8 @@ use rfc8620::*;
|
|||
pub mod objects;
|
||||
use objects::*;
|
||||
|
||||
pub mod folder;
|
||||
use folder::*;
|
||||
pub mod mailbox;
|
||||
use mailbox::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct EnvelopeCache {
|
||||
|
@ -189,7 +189,7 @@ pub struct JmapType {
|
|||
connection: Arc<JmapConnection>,
|
||||
store: Arc<RwLock<Store>>,
|
||||
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||
folders: Arc<RwLock<FnvHashMap<FolderHash, JmapFolder>>>,
|
||||
mailboxes: Arc<RwLock<FnvHashMap<MailboxHash, JmapMailbox>>>,
|
||||
}
|
||||
|
||||
impl MailBackend for JmapType {
|
||||
|
@ -202,18 +202,18 @@ impl MailBackend for JmapType {
|
|||
if Instant::now().duration_since(self.online.lock().unwrap().0)
|
||||
>= std::time::Duration::new(2, 0)
|
||||
{
|
||||
let _ = self.folders();
|
||||
let _ = self.mailboxes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||
fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
|
||||
let mut w = AsyncBuilder::new();
|
||||
let folders = self.folders.clone();
|
||||
let mailboxes = self.mailboxes.clone();
|
||||
let store = self.store.clone();
|
||||
let tag_index = self.tag_index.clone();
|
||||
let connection = self.connection.clone();
|
||||
let folder_hash = folder.hash();
|
||||
let mailbox_hash = mailbox.hash();
|
||||
let handle = {
|
||||
let tx = w.tx();
|
||||
let closure = move |_work_context| {
|
||||
|
@ -221,7 +221,7 @@ impl MailBackend for JmapType {
|
|||
&connection,
|
||||
&store,
|
||||
&tag_index,
|
||||
&folders.read().unwrap()[&folder_hash],
|
||||
&mailboxes.read().unwrap()[&mailbox_hash],
|
||||
)))
|
||||
.unwrap();
|
||||
tx.send(AsyncStatus::Finished).unwrap();
|
||||
|
@ -239,19 +239,19 @@ impl MailBackend for JmapType {
|
|||
Err(MeliError::from("sadfsa"))
|
||||
}
|
||||
|
||||
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
|
||||
if self.folders.read().unwrap().is_empty() {
|
||||
let folders = std::dbg!(protocol::get_mailboxes(&self.connection))?;
|
||||
*self.folders.write().unwrap() = folders;
|
||||
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||
if self.mailboxes.read().unwrap().is_empty() {
|
||||
let mailboxes = std::dbg!(protocol::get_mailboxes(&self.connection))?;
|
||||
*self.mailboxes.write().unwrap() = mailboxes;
|
||||
}
|
||||
|
||||
Ok(self
|
||||
.folders
|
||||
.mailboxes
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.filter(|(_, f)| f.is_subscribed)
|
||||
.map(|(&h, f)| (h, BackendFolder::clone(f) as Folder))
|
||||
.map(|(&h, f)| (h, BackendMailbox::clone(f) as Mailbox))
|
||||
.collect())
|
||||
}
|
||||
|
||||
|
@ -263,7 +263,7 @@ impl MailBackend for JmapType {
|
|||
))
|
||||
}
|
||||
|
||||
fn save(&self, _bytes: &[u8], _folder: &str, _flags: Option<Flag>) -> Result<()> {
|
||||
fn save(&self, _bytes: &[u8], _mailbox: &str, _flags: Option<Flag>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -291,7 +291,7 @@ impl JmapType {
|
|||
connection: Arc::new(JmapConnection::new(&server_conf, online.clone())?),
|
||||
store: Arc::new(RwLock::new(Store::default())),
|
||||
tag_index: Arc::new(RwLock::new(Default::default())),
|
||||
folders: Arc::new(RwLock::new(FnvHashMap::default())),
|
||||
mailboxes: Arc::new(RwLock::new(FnvHashMap::default())),
|
||||
account_name: s.name.clone(),
|
||||
online,
|
||||
is_subscribed: Arc::new(IsSubscribedFn(is_subscribed)),
|
||||
|
|
|
@ -20,15 +20,15 @@
|
|||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::backends::{FolderPermissions, SpecialUsageMailbox};
|
||||
use crate::backends::{MailboxPermissions, SpecialUsageMailbox};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JmapFolder {
|
||||
pub struct JmapMailbox {
|
||||
pub name: String,
|
||||
pub path: String,
|
||||
pub hash: FolderHash,
|
||||
pub v: Vec<FolderHash>,
|
||||
pub hash: MailboxHash,
|
||||
pub v: Vec<MailboxHash>,
|
||||
pub id: String,
|
||||
pub is_subscribed: bool,
|
||||
pub my_rights: JmapRights,
|
||||
|
@ -42,8 +42,8 @@ pub struct JmapFolder {
|
|||
pub usage: Arc<RwLock<SpecialUsageMailbox>>,
|
||||
}
|
||||
|
||||
impl BackendFolder for JmapFolder {
|
||||
fn hash(&self) -> FolderHash {
|
||||
impl BackendMailbox for JmapMailbox {
|
||||
fn hash(&self) -> MailboxHash {
|
||||
self.hash
|
||||
}
|
||||
|
||||
|
@ -57,20 +57,20 @@ impl BackendFolder for JmapFolder {
|
|||
|
||||
fn change_name(&mut self, _s: &str) {}
|
||||
|
||||
fn clone(&self) -> Folder {
|
||||
fn clone(&self) -> Mailbox {
|
||||
Box::new(std::clone::Clone::clone(self))
|
||||
}
|
||||
|
||||
fn children(&self) -> &[FolderHash] {
|
||||
fn children(&self) -> &[MailboxHash] {
|
||||
&self.v
|
||||
}
|
||||
|
||||
fn parent(&self) -> Option<FolderHash> {
|
||||
fn parent(&self) -> Option<MailboxHash> {
|
||||
None
|
||||
}
|
||||
|
||||
fn permissions(&self) -> FolderPermissions {
|
||||
FolderPermissions::default()
|
||||
fn permissions(&self) -> MailboxPermissions {
|
||||
MailboxPermissions::default()
|
||||
}
|
||||
|
||||
fn special_usage(&self) -> SpecialUsageMailbox {
|
||||
|
@ -83,7 +83,7 @@ impl BackendFolder for JmapFolder {
|
|||
Some("sent") => SpecialUsageMailbox::Sent,
|
||||
Some(other) => {
|
||||
debug!(
|
||||
"unknown JMAP mailbox role for folder {}: {}",
|
||||
"unknown JMAP mailbox role for mailbox {}: {}",
|
||||
self.path(),
|
||||
other
|
||||
);
|
|
@ -394,7 +394,7 @@ pub struct EmailQueryResponse {
|
|||
pub struct EmailQuery {
|
||||
#[serde(flatten)]
|
||||
pub query_call: Query<EmailFilterCondition, EmailObject>,
|
||||
//pub filter: EmailFilterCondition, /* "inMailboxes": [ folder.id ] },*/
|
||||
//pub filter: EmailFilterCondition, /* "inMailboxes": [ mailbox.id ] },*/
|
||||
pub collapse_threads: bool,
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use super::folder::JmapFolder;
|
||||
use super::mailbox::JmapMailbox;
|
||||
use super::*;
|
||||
use serde::Serialize;
|
||||
use serde_json::{json, Value};
|
||||
|
@ -94,7 +94,7 @@ impl Request {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_mailboxes(conn: &JmapConnection) -> Result<FnvHashMap<FolderHash, JmapFolder>> {
|
||||
pub fn get_mailboxes(conn: &JmapConnection) -> Result<FnvHashMap<MailboxHash, JmapMailbox>> {
|
||||
let seq = get_request_no!(conn.request_no);
|
||||
let res = conn
|
||||
.client
|
||||
|
@ -141,7 +141,7 @@ pub fn get_mailboxes(conn: &JmapConnection) -> Result<FnvHashMap<FolderHash, Jma
|
|||
let hash = crate::get_path_hash!(&name);
|
||||
(
|
||||
hash,
|
||||
JmapFolder {
|
||||
JmapMailbox {
|
||||
name: name.clone(),
|
||||
hash,
|
||||
path: name,
|
||||
|
@ -170,12 +170,12 @@ pub struct JsonResponse<'a> {
|
|||
method_responses: Vec<MethodResponse<'a>>,
|
||||
}
|
||||
|
||||
pub fn get_message_list(conn: &JmapConnection, folder: &JmapFolder) -> Result<Vec<String>> {
|
||||
pub fn get_message_list(conn: &JmapConnection, mailbox: &JmapMailbox) -> Result<Vec<String>> {
|
||||
let email_call: EmailQuery = EmailQuery::new(
|
||||
Query::new()
|
||||
.account_id(conn.mail_account_id().to_string())
|
||||
.filter(Some(
|
||||
EmailFilterCondition::new().in_mailbox(Some(folder.id.clone())),
|
||||
EmailFilterCondition::new().in_mailbox(Some(mailbox.id.clone())),
|
||||
))
|
||||
.position(0),
|
||||
)
|
||||
|
@ -241,13 +241,13 @@ pub fn get(
|
|||
conn: &JmapConnection,
|
||||
store: &Arc<RwLock<Store>>,
|
||||
tag_index: &Arc<RwLock<BTreeMap<u64, String>>>,
|
||||
folder: &JmapFolder,
|
||||
mailbox: &JmapMailbox,
|
||||
) -> Result<Vec<Envelope>> {
|
||||
let email_query_call: EmailQuery = EmailQuery::new(
|
||||
Query::new()
|
||||
.account_id(conn.mail_account_id().to_string())
|
||||
.filter(Some(
|
||||
EmailFilterCondition::new().in_mailbox(Some(folder.id.clone())),
|
||||
EmailFilterCondition::new().in_mailbox(Some(mailbox.id.clone())),
|
||||
))
|
||||
.position(0),
|
||||
)
|
||||
|
|
|
@ -39,7 +39,7 @@ use std::sync::{Arc, Mutex};
|
|||
#[derive(Debug)]
|
||||
pub struct MaildirOp {
|
||||
hash_index: HashIndexes,
|
||||
folder_hash: FolderHash,
|
||||
mailbox_hash: MailboxHash,
|
||||
hash: EnvelopeHash,
|
||||
slice: Option<Mmap>,
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ impl Clone for MaildirOp {
|
|||
fn clone(&self) -> Self {
|
||||
MaildirOp {
|
||||
hash_index: self.hash_index.clone(),
|
||||
folder_hash: self.folder_hash,
|
||||
mailbox_hash: self.mailbox_hash,
|
||||
hash: self.hash,
|
||||
slice: None,
|
||||
}
|
||||
|
@ -56,18 +56,18 @@ impl Clone for MaildirOp {
|
|||
}
|
||||
|
||||
impl MaildirOp {
|
||||
pub fn new(hash: EnvelopeHash, hash_index: HashIndexes, folder_hash: FolderHash) -> Self {
|
||||
pub fn new(hash: EnvelopeHash, hash_index: HashIndexes, mailbox_hash: MailboxHash) -> Self {
|
||||
MaildirOp {
|
||||
hash_index,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
hash,
|
||||
slice: None,
|
||||
}
|
||||
}
|
||||
fn path(&self) -> PathBuf {
|
||||
let map = self.hash_index.lock().unwrap();
|
||||
let map = &map[&self.folder_hash];
|
||||
debug!("looking for {} in {} map", self.hash, self.folder_hash);
|
||||
let map = &map[&self.mailbox_hash];
|
||||
debug!("looking for {} in {} map", self.hash, self.mailbox_hash);
|
||||
if !map.contains_key(&self.hash) {
|
||||
debug!("doesn't contain it though len = {}\n{:#?}", map.len(), map);
|
||||
for e in map.iter() {
|
||||
|
@ -155,7 +155,7 @@ impl<'a> BackendOp for MaildirOp {
|
|||
let new_name: PathBuf = new_name.into();
|
||||
let hash_index = self.hash_index.clone();
|
||||
let mut map = hash_index.lock().unwrap();
|
||||
let map = map.entry(self.folder_hash).or_default();
|
||||
let map = map.entry(self.mailbox_hash).or_default();
|
||||
map.entry(old_hash).or_default().modified = Some(PathMod::Path(new_name.clone()));
|
||||
|
||||
debug!("renaming {:?} to {:?}", path, new_name);
|
||||
|
@ -170,37 +170,37 @@ impl<'a> BackendOp for MaildirOp {
|
|||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct MaildirFolder {
|
||||
hash: FolderHash,
|
||||
pub struct MaildirMailbox {
|
||||
hash: MailboxHash,
|
||||
name: String,
|
||||
fs_path: PathBuf,
|
||||
path: PathBuf,
|
||||
parent: Option<FolderHash>,
|
||||
children: Vec<FolderHash>,
|
||||
parent: Option<MailboxHash>,
|
||||
children: Vec<MailboxHash>,
|
||||
pub usage: Arc<RwLock<SpecialUsageMailbox>>,
|
||||
pub is_subscribed: bool,
|
||||
permissions: FolderPermissions,
|
||||
permissions: MailboxPermissions,
|
||||
pub total: Arc<Mutex<usize>>,
|
||||
pub unseen: Arc<Mutex<usize>>,
|
||||
}
|
||||
|
||||
impl MaildirFolder {
|
||||
impl MaildirMailbox {
|
||||
pub fn new(
|
||||
path: String,
|
||||
file_name: String,
|
||||
parent: Option<FolderHash>,
|
||||
children: Vec<FolderHash>,
|
||||
parent: Option<MailboxHash>,
|
||||
children: Vec<MailboxHash>,
|
||||
settings: &AccountSettings,
|
||||
) -> Result<Self> {
|
||||
let pathbuf = PathBuf::from(&path);
|
||||
let mut h = DefaultHasher::new();
|
||||
pathbuf.hash(&mut h);
|
||||
|
||||
/* Check if folder path (Eg `INBOX/Lists/luddites`) is included in the subscribed
|
||||
/* Check if mailbox path (Eg `INBOX/Lists/luddites`) is included in the subscribed
|
||||
* mailboxes in user configuration */
|
||||
let fname = pathbuf
|
||||
.strip_prefix(
|
||||
PathBuf::from(&settings.root_folder)
|
||||
PathBuf::from(&settings.root_mailbox)
|
||||
.expand()
|
||||
.parent()
|
||||
.unwrap_or_else(|| &Path::new("/")),
|
||||
|
@ -213,7 +213,7 @@ impl MaildirFolder {
|
|||
true
|
||||
};
|
||||
|
||||
let ret = MaildirFolder {
|
||||
let ret = MaildirMailbox {
|
||||
hash: h.finish(),
|
||||
name: file_name,
|
||||
path: fname.unwrap().to_path_buf(),
|
||||
|
@ -222,7 +222,7 @@ impl MaildirFolder {
|
|||
children,
|
||||
usage: Arc::new(RwLock::new(SpecialUsageMailbox::Normal)),
|
||||
is_subscribed: false,
|
||||
permissions: FolderPermissions {
|
||||
permissions: MailboxPermissions {
|
||||
create_messages: !read_only,
|
||||
remove_messages: !read_only,
|
||||
set_flags: !read_only,
|
||||
|
@ -250,7 +250,7 @@ impl MaildirFolder {
|
|||
p.push(d);
|
||||
if !p.is_dir() {
|
||||
return Err(MeliError::new(format!(
|
||||
"{} is not a valid maildir folder",
|
||||
"{} is not a valid maildir mailbox",
|
||||
path.display()
|
||||
)));
|
||||
}
|
||||
|
@ -260,8 +260,8 @@ impl MaildirFolder {
|
|||
}
|
||||
}
|
||||
|
||||
impl BackendFolder for MaildirFolder {
|
||||
fn hash(&self) -> FolderHash {
|
||||
impl BackendMailbox for MaildirMailbox {
|
||||
fn hash(&self) -> MailboxHash {
|
||||
self.hash
|
||||
}
|
||||
|
||||
|
@ -277,11 +277,11 @@ impl BackendFolder for MaildirFolder {
|
|||
self.name = s.to_string();
|
||||
}
|
||||
|
||||
fn children(&self) -> &[FolderHash] {
|
||||
fn children(&self) -> &[MailboxHash] {
|
||||
&self.children
|
||||
}
|
||||
|
||||
fn clone(&self) -> Folder {
|
||||
fn clone(&self) -> Mailbox {
|
||||
Box::new(std::clone::Clone::clone(self))
|
||||
}
|
||||
|
||||
|
@ -289,11 +289,11 @@ impl BackendFolder for MaildirFolder {
|
|||
*self.usage.read().unwrap()
|
||||
}
|
||||
|
||||
fn parent(&self) -> Option<FolderHash> {
|
||||
fn parent(&self) -> Option<MailboxHash> {
|
||||
self.parent
|
||||
}
|
||||
|
||||
fn permissions(&self) -> FolderPermissions {
|
||||
fn permissions(&self) -> MailboxPermissions {
|
||||
self.permissions
|
||||
}
|
||||
fn is_subscribed(&self) -> bool {
|
||||
|
|
|
@ -20,10 +20,10 @@
|
|||
*/
|
||||
|
||||
use super::{
|
||||
BackendFolder, BackendOp, Folder, FolderHash, MailBackend, RefreshEvent, RefreshEventConsumer,
|
||||
RefreshEventKind::*,
|
||||
BackendMailbox, BackendOp, MailBackend, Mailbox, MailboxHash, RefreshEvent,
|
||||
RefreshEventConsumer, RefreshEventKind::*,
|
||||
};
|
||||
use super::{MaildirFolder, MaildirOp};
|
||||
use super::{MaildirMailbox, MaildirOp};
|
||||
use crate::async_workers::*;
|
||||
use crate::conf::AccountSettings;
|
||||
use crate::email::{Envelope, EnvelopeHash, Flag};
|
||||
|
@ -88,7 +88,7 @@ impl From<PathBuf> for MaildirPath {
|
|||
#[derive(Debug, Default)]
|
||||
pub struct HashIndex {
|
||||
index: FnvHashMap<EnvelopeHash, MaildirPath>,
|
||||
hash: FolderHash,
|
||||
hash: MailboxHash,
|
||||
}
|
||||
|
||||
impl Deref for HashIndex {
|
||||
|
@ -104,14 +104,14 @@ impl DerefMut for HashIndex {
|
|||
}
|
||||
}
|
||||
|
||||
pub type HashIndexes = Arc<Mutex<FnvHashMap<FolderHash, HashIndex>>>;
|
||||
pub type HashIndexes = Arc<Mutex<FnvHashMap<MailboxHash, HashIndex>>>;
|
||||
|
||||
/// Maildir backend https://cr.yp.to/proto/maildir.html
|
||||
#[derive(Debug)]
|
||||
pub struct MaildirType {
|
||||
name: String,
|
||||
folders: FnvHashMap<FolderHash, MaildirFolder>,
|
||||
folder_index: Arc<Mutex<FnvHashMap<EnvelopeHash, FolderHash>>>,
|
||||
mailboxes: FnvHashMap<MailboxHash, MaildirMailbox>,
|
||||
mailbox_index: Arc<Mutex<FnvHashMap<EnvelopeHash, MailboxHash>>>,
|
||||
hash_indexes: HashIndexes,
|
||||
path: PathBuf,
|
||||
}
|
||||
|
@ -181,32 +181,32 @@ impl MailBackend for MaildirType {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
|
||||
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||
Ok(self
|
||||
.folders
|
||||
.mailboxes
|
||||
.iter()
|
||||
.map(|(h, f)| (*h, BackendFolder::clone(f)))
|
||||
.map(|(h, f)| (*h, BackendMailbox::clone(f)))
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||
self.multicore(4, folder)
|
||||
fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
|
||||
self.multicore(4, mailbox)
|
||||
}
|
||||
fn refresh(
|
||||
&mut self,
|
||||
folder_hash: FolderHash,
|
||||
mailbox_hash: MailboxHash,
|
||||
sender: RefreshEventConsumer,
|
||||
) -> Result<Async<()>> {
|
||||
let w = AsyncBuilder::new();
|
||||
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
|
||||
|
||||
let handle = {
|
||||
let folder: &MaildirFolder = &self.folders[&folder_hash];
|
||||
let path: PathBuf = folder.fs_path().into();
|
||||
let name = format!("refresh {:?}", folder.name());
|
||||
let mailbox: &MaildirMailbox = &self.mailboxes[&mailbox_hash];
|
||||
let path: PathBuf = mailbox.fs_path().into();
|
||||
let name = format!("refresh {:?}", mailbox.name());
|
||||
let root_path = self.path.to_path_buf();
|
||||
let map = self.hash_indexes.clone();
|
||||
let folder_index = self.folder_index.clone();
|
||||
let mailbox_index = self.mailbox_index.clone();
|
||||
|
||||
Box::new(move |work_context: crate::async_workers::WorkContext| {
|
||||
work_context
|
||||
|
@ -237,14 +237,14 @@ impl MailBackend for MaildirType {
|
|||
}
|
||||
let mut current_hashes = {
|
||||
let mut map = map.lock().unwrap();
|
||||
let map = map.entry(folder_hash).or_default();
|
||||
let map = map.entry(mailbox_hash).or_default();
|
||||
map.keys().cloned().collect::<FnvHashSet<EnvelopeHash>>()
|
||||
};
|
||||
for file in files {
|
||||
let hash = get_file_hash(&file);
|
||||
{
|
||||
let mut map = map.lock().unwrap();
|
||||
let map = map.entry(folder_hash).or_default();
|
||||
let map = map.entry(mailbox_hash).or_default();
|
||||
if map.contains_key(&hash) {
|
||||
map.remove(&hash);
|
||||
current_hashes.remove(&hash);
|
||||
|
@ -252,9 +252,9 @@ impl MailBackend for MaildirType {
|
|||
}
|
||||
(*map).insert(hash, PathBuf::from(&file).into());
|
||||
}
|
||||
let op = Box::new(MaildirOp::new(hash, map.clone(), folder_hash));
|
||||
let op = Box::new(MaildirOp::new(hash, map.clone(), mailbox_hash));
|
||||
if let Some(e) = Envelope::from_token(op, hash) {
|
||||
folder_index.lock().unwrap().insert(e.hash(), folder_hash);
|
||||
mailbox_index.lock().unwrap().insert(e.hash(), mailbox_hash);
|
||||
let file_name = PathBuf::from(file)
|
||||
.strip_prefix(&root_path)
|
||||
.unwrap()
|
||||
|
@ -277,7 +277,7 @@ impl MailBackend for MaildirType {
|
|||
bincode::serialize_into(writer, &e).unwrap();
|
||||
}
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: Create(Box::new(e)),
|
||||
});
|
||||
} else {
|
||||
|
@ -290,7 +290,7 @@ impl MailBackend for MaildirType {
|
|||
}
|
||||
}
|
||||
for ev in current_hashes.into_iter().map(|h| RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: Remove(h),
|
||||
}) {
|
||||
sender.send(ev);
|
||||
|
@ -299,7 +299,7 @@ impl MailBackend for MaildirType {
|
|||
};
|
||||
if let Err(err) = thunk(&sender) {
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: Failure(err),
|
||||
});
|
||||
}
|
||||
|
@ -319,14 +319,14 @@ impl MailBackend for MaildirType {
|
|||
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
|
||||
debug!("watching {:?}", root_path);
|
||||
let hash_indexes = self.hash_indexes.clone();
|
||||
let folder_index = self.folder_index.clone();
|
||||
let folder_counts = self
|
||||
.folders
|
||||
let mailbox_index = self.mailbox_index.clone();
|
||||
let mailbox_counts = self
|
||||
.mailboxes
|
||||
.iter()
|
||||
.map(|(&k, v)| (k, (v.unseen.clone(), v.total.clone())))
|
||||
.collect::<FnvHashMap<FolderHash, (Arc<Mutex<usize>>, Arc<Mutex<usize>>)>>();
|
||||
.collect::<FnvHashMap<MailboxHash, (Arc<Mutex<usize>>, Arc<Mutex<usize>>)>>();
|
||||
let handle = thread::Builder::new()
|
||||
.name("folder watch".to_string())
|
||||
.name("mailbox watch".to_string())
|
||||
.spawn(move || {
|
||||
// Move `watcher` in the closure's scope so that it doesn't get dropped.
|
||||
let _watcher = watcher;
|
||||
|
@ -360,7 +360,7 @@ impl MailBackend for MaildirType {
|
|||
|
||||
|
||||
}
|
||||
let folder_hash = get_path_hash!(pathbuf);
|
||||
let mailbox_hash = get_path_hash!(pathbuf);
|
||||
let file_name = pathbuf
|
||||
.as_path()
|
||||
.strip_prefix(&root_path)
|
||||
|
@ -368,12 +368,12 @@ impl MailBackend for MaildirType {
|
|||
.to_path_buf();
|
||||
if let Some(env) = add_path_to_index(
|
||||
&hash_indexes,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
pathbuf.as_path(),
|
||||
&cache_dir,
|
||||
file_name,
|
||||
) {
|
||||
folder_index.lock().unwrap().insert(env.hash(),folder_hash);
|
||||
mailbox_index.lock().unwrap().insert(env.hash(),mailbox_hash);
|
||||
debug!(
|
||||
"Create event {} {} {}",
|
||||
env.hash(),
|
||||
|
@ -381,11 +381,11 @@ impl MailBackend for MaildirType {
|
|||
pathbuf.display()
|
||||
);
|
||||
if !env.is_seen() {
|
||||
*folder_counts[&folder_hash].0.lock().unwrap() += 1;
|
||||
*mailbox_counts[&mailbox_hash].0.lock().unwrap() += 1;
|
||||
}
|
||||
*folder_counts[&folder_hash].1.lock().unwrap() += 1;
|
||||
*mailbox_counts[&mailbox_hash].1.lock().unwrap() += 1;
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: Create(Box::new(env)),
|
||||
});
|
||||
}
|
||||
|
@ -394,10 +394,10 @@ impl MailBackend for MaildirType {
|
|||
DebouncedEvent::NoticeWrite(pathbuf)
|
||||
| DebouncedEvent::Write(pathbuf) => {
|
||||
debug!("DebouncedEvent::Write(path = {:?}", &pathbuf);
|
||||
let folder_hash = get_path_hash!(pathbuf);
|
||||
let mailbox_hash = get_path_hash!(pathbuf);
|
||||
let mut hash_indexes_lock = hash_indexes.lock().unwrap();
|
||||
let index_lock =
|
||||
&mut hash_indexes_lock.entry(folder_hash).or_default();
|
||||
&mut hash_indexes_lock.entry(mailbox_hash).or_default();
|
||||
let file_name = pathbuf
|
||||
.as_path()
|
||||
.strip_prefix(&root_path)
|
||||
|
@ -418,14 +418,14 @@ impl MailBackend for MaildirType {
|
|||
* envelope. */
|
||||
if let Some(env) = add_path_to_index(
|
||||
&hash_indexes,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
pathbuf.as_path(),
|
||||
&cache_dir,
|
||||
file_name,
|
||||
) {
|
||||
folder_index.lock().unwrap().insert(env.hash(),folder_hash);
|
||||
mailbox_index.lock().unwrap().insert(env.hash(),mailbox_hash);
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: Create(Box::new(env)),
|
||||
});
|
||||
}
|
||||
|
@ -438,7 +438,7 @@ impl MailBackend for MaildirType {
|
|||
let op = Box::new(MaildirOp::new(
|
||||
new_hash,
|
||||
hash_indexes.clone(),
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
));
|
||||
if let Some(env) = Envelope::from_token(op, new_hash) {
|
||||
debug!("{}\t{:?}", new_hash, &pathbuf);
|
||||
|
@ -451,7 +451,7 @@ impl MailBackend for MaildirType {
|
|||
/* Send Write notice */
|
||||
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: Update(old_hash, Box::new(env)),
|
||||
});
|
||||
}
|
||||
|
@ -461,9 +461,9 @@ impl MailBackend for MaildirType {
|
|||
DebouncedEvent::NoticeRemove(pathbuf)
|
||||
| DebouncedEvent::Remove(pathbuf) => {
|
||||
debug!("DebouncedEvent::Remove(path = {:?}", pathbuf);
|
||||
let folder_hash = get_path_hash!(pathbuf);
|
||||
let mailbox_hash = get_path_hash!(pathbuf);
|
||||
let mut hash_indexes_lock = hash_indexes.lock().unwrap();
|
||||
let index_lock = hash_indexes_lock.entry(folder_hash).or_default();
|
||||
let index_lock = hash_indexes_lock.entry(mailbox_hash).or_default();
|
||||
let hash: EnvelopeHash = if let Some((k, _)) =
|
||||
index_lock.iter().find(|(_, v)| *v.buf == pathbuf)
|
||||
{
|
||||
|
@ -493,9 +493,9 @@ impl MailBackend for MaildirType {
|
|||
});
|
||||
|
||||
//FIXME: check if envelope was unseen to update unseen count
|
||||
*folder_counts[&folder_hash].1.lock().unwrap() += 1;
|
||||
*mailbox_counts[&mailbox_hash].1.lock().unwrap() += 1;
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: Remove(hash),
|
||||
});
|
||||
}
|
||||
|
@ -505,12 +505,12 @@ impl MailBackend for MaildirType {
|
|||
"DebouncedEvent::Rename(src = {:?}, dest = {:?})",
|
||||
src, dest
|
||||
);
|
||||
let folder_hash = get_path_hash!(src);
|
||||
let mailbox_hash = get_path_hash!(src);
|
||||
let old_hash: EnvelopeHash = get_file_hash(src.as_path());
|
||||
let new_hash: EnvelopeHash = get_file_hash(dest.as_path());
|
||||
|
||||
let mut hash_indexes_lock = hash_indexes.lock().unwrap();
|
||||
let index_lock = hash_indexes_lock.entry(folder_hash).or_default();
|
||||
let index_lock = hash_indexes_lock.entry(mailbox_hash).or_default();
|
||||
|
||||
if index_lock.contains_key(&old_hash)
|
||||
&& !index_lock[&old_hash].removed
|
||||
|
@ -524,7 +524,7 @@ impl MailBackend for MaildirType {
|
|||
hash: get_path_hash!(dest),
|
||||
kind: Rename(old_hash, new_hash),
|
||||
});
|
||||
folder_index.lock().unwrap().insert(new_hash,get_path_hash!(dest) );
|
||||
mailbox_index.lock().unwrap().insert(new_hash,get_path_hash!(dest) );
|
||||
index_lock.insert(new_hash, dest.into());
|
||||
continue;
|
||||
} else if !index_lock.contains_key(&new_hash)
|
||||
|
@ -556,12 +556,12 @@ impl MailBackend for MaildirType {
|
|||
drop(hash_indexes_lock);
|
||||
if let Some(env) = add_path_to_index(
|
||||
&hash_indexes,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
dest.as_path(),
|
||||
&cache_dir,
|
||||
file_name,
|
||||
) {
|
||||
folder_index.lock().unwrap().insert(env.hash(),folder_hash);
|
||||
mailbox_index.lock().unwrap().insert(env.hash(), mailbox_hash);
|
||||
debug!(
|
||||
"Create event {} {} {}",
|
||||
env.hash(),
|
||||
|
@ -569,11 +569,11 @@ impl MailBackend for MaildirType {
|
|||
dest.display()
|
||||
);
|
||||
if !env.is_seen() {
|
||||
*folder_counts[&folder_hash].0.lock().unwrap() += 1;
|
||||
*mailbox_counts[&mailbox_hash].0.lock().unwrap() += 1;
|
||||
}
|
||||
*folder_counts[&folder_hash].1.lock().unwrap() += 1;
|
||||
*mailbox_counts[&mailbox_hash].1.lock().unwrap() += 1;
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: Create(Box::new(env)),
|
||||
});
|
||||
continue;
|
||||
|
@ -595,10 +595,10 @@ impl MailBackend for MaildirType {
|
|||
});
|
||||
*/
|
||||
}
|
||||
/* Trigger rescan of folder */
|
||||
/* Trigger rescan of mailbox */
|
||||
DebouncedEvent::Rescan => {
|
||||
/* Actually should rescan all folders */
|
||||
unreachable!("Unimplemented: rescan of all folders in MaildirType")
|
||||
/* Actually should rescan all mailboxes */
|
||||
unreachable!("Unimplemented: rescan of all mailboxes in MaildirType")
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
|
@ -613,20 +613,20 @@ impl MailBackend for MaildirType {
|
|||
Box::new(MaildirOp::new(
|
||||
hash,
|
||||
self.hash_indexes.clone(),
|
||||
self.folder_index.lock().unwrap()[&hash],
|
||||
self.mailbox_index.lock().unwrap()[&hash],
|
||||
))
|
||||
}
|
||||
|
||||
fn save(&self, bytes: &[u8], folder: &str, flags: Option<Flag>) -> Result<()> {
|
||||
for f in self.folders.values() {
|
||||
if f.name == folder || f.path.to_str().unwrap() == folder {
|
||||
return MaildirType::save_to_folder(f.fs_path.clone(), bytes, flags);
|
||||
fn save(&self, bytes: &[u8], mailbox: &str, flags: Option<Flag>) -> Result<()> {
|
||||
for f in self.mailboxes.values() {
|
||||
if f.name == mailbox || f.path.to_str().unwrap() == mailbox {
|
||||
return MaildirType::save_to_mailbox(f.fs_path.clone(), bytes, flags);
|
||||
}
|
||||
}
|
||||
|
||||
Err(MeliError::new(format!(
|
||||
"'{}' is not a valid folder.",
|
||||
folder
|
||||
"'{}' is not a valid mailbox.",
|
||||
mailbox
|
||||
)))
|
||||
}
|
||||
|
||||
|
@ -634,35 +634,35 @@ impl MailBackend for MaildirType {
|
|||
self
|
||||
}
|
||||
|
||||
fn create_folder(
|
||||
fn create_mailbox(
|
||||
&mut self,
|
||||
new_path: String,
|
||||
) -> Result<(FolderHash, FnvHashMap<FolderHash, Folder>)> {
|
||||
) -> Result<(MailboxHash, FnvHashMap<MailboxHash, Mailbox>)> {
|
||||
let mut path = self.path.clone();
|
||||
path.push(&new_path);
|
||||
if !path.starts_with(&self.path) {
|
||||
return Err(MeliError::new(format!("Path given (`{}`) is absolute. Please provide a path relative to the account's root folder.", &new_path)));
|
||||
return Err(MeliError::new(format!("Path given (`{}`) is absolute. Please provide a path relative to the account's root mailbox.", &new_path)));
|
||||
}
|
||||
|
||||
std::fs::create_dir(&path)?;
|
||||
/* create_dir does not create intermediate directories (like `mkdir -p`), so the parent must be a valid
|
||||
* folder at this point. */
|
||||
* mailbox at this point. */
|
||||
|
||||
let parent = path.parent().and_then(|p| {
|
||||
self.folders
|
||||
self.mailboxes
|
||||
.iter()
|
||||
.find(|(_, f)| f.fs_path == p)
|
||||
.map(|item| *item.0)
|
||||
});
|
||||
|
||||
let folder_hash = get_path_hash!(&path);
|
||||
let mailbox_hash = get_path_hash!(&path);
|
||||
if let Some(parent) = parent {
|
||||
self.folders
|
||||
self.mailboxes
|
||||
.entry(parent)
|
||||
.and_modify(|entry| entry.children.push(folder_hash));
|
||||
.and_modify(|entry| entry.children.push(mailbox_hash));
|
||||
}
|
||||
let new_folder = MaildirFolder {
|
||||
hash: folder_hash,
|
||||
let new_mailbox = MaildirMailbox {
|
||||
hash: mailbox_hash,
|
||||
path: PathBuf::from(&new_path),
|
||||
name: new_path,
|
||||
fs_path: path,
|
||||
|
@ -675,29 +675,29 @@ impl MailBackend for MaildirType {
|
|||
total: Default::default(),
|
||||
};
|
||||
|
||||
self.folders.insert(folder_hash, new_folder);
|
||||
Ok((folder_hash, self.folders()?))
|
||||
self.mailboxes.insert(mailbox_hash, new_mailbox);
|
||||
Ok((mailbox_hash, self.mailboxes()?))
|
||||
}
|
||||
|
||||
fn delete_folder(
|
||||
fn delete_mailbox(
|
||||
&mut self,
|
||||
_folder_hash: FolderHash,
|
||||
) -> Result<FnvHashMap<FolderHash, Folder>> {
|
||||
_mailbox_hash: MailboxHash,
|
||||
) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||
Err(MeliError::new("Unimplemented."))
|
||||
}
|
||||
|
||||
fn set_folder_subscription(&mut self, _folder_hash: FolderHash, _val: bool) -> Result<()> {
|
||||
fn set_mailbox_subscription(&mut self, _mailbox_hash: MailboxHash, _val: bool) -> Result<()> {
|
||||
Err(MeliError::new("Unimplemented."))
|
||||
}
|
||||
|
||||
fn rename_folder(&mut self, _folder_hash: FolderHash, _new_path: String) -> Result<Folder> {
|
||||
fn rename_mailbox(&mut self, _mailbox_hash: MailboxHash, _new_path: String) -> Result<Mailbox> {
|
||||
Err(MeliError::new("Unimplemented."))
|
||||
}
|
||||
|
||||
fn set_folder_permissions(
|
||||
fn set_mailbox_permissions(
|
||||
&mut self,
|
||||
_folder_hash: FolderHash,
|
||||
_val: crate::backends::FolderPermissions,
|
||||
_mailbox_hash: MailboxHash,
|
||||
_val: crate::backends::MailboxPermissions,
|
||||
) -> Result<()> {
|
||||
Err(MeliError::new("Unimplemented."))
|
||||
}
|
||||
|
@ -708,12 +708,12 @@ impl MaildirType {
|
|||
settings: &AccountSettings,
|
||||
is_subscribed: Box<dyn Fn(&str) -> bool>,
|
||||
) -> Result<Box<dyn MailBackend>> {
|
||||
let mut folders: FnvHashMap<FolderHash, MaildirFolder> = Default::default();
|
||||
fn recurse_folders<P: AsRef<Path>>(
|
||||
folders: &mut FnvHashMap<FolderHash, MaildirFolder>,
|
||||
let mut mailboxes: FnvHashMap<MailboxHash, MaildirMailbox> = Default::default();
|
||||
fn recurse_mailboxes<P: AsRef<Path>>(
|
||||
mailboxes: &mut FnvHashMap<MailboxHash, MaildirMailbox>,
|
||||
settings: &AccountSettings,
|
||||
p: P,
|
||||
) -> Result<Vec<FolderHash>> {
|
||||
) -> Result<Vec<MailboxHash>> {
|
||||
if !p.as_ref().exists() || !p.as_ref().is_dir() {
|
||||
return Err(MeliError::new(format!(
|
||||
"Configuration error: Path \"{}\" {}",
|
||||
|
@ -734,20 +734,20 @@ impl MaildirType {
|
|||
continue 'entries;
|
||||
}
|
||||
if path.is_dir() {
|
||||
if let Ok(mut f) = MaildirFolder::new(
|
||||
if let Ok(mut f) = MaildirMailbox::new(
|
||||
path.to_str().unwrap().to_string(),
|
||||
path.file_name().unwrap().to_str().unwrap().to_string(),
|
||||
None,
|
||||
Vec::new(),
|
||||
&settings,
|
||||
) {
|
||||
f.children = recurse_folders(folders, settings, &path)?;
|
||||
f.children = recurse_mailboxes(mailboxes, settings, &path)?;
|
||||
f.children
|
||||
.iter()
|
||||
.map(|c| folders.get_mut(c).map(|f| f.parent = Some(f.hash)))
|
||||
.map(|c| mailboxes.get_mut(c).map(|f| f.parent = Some(f.hash)))
|
||||
.count();
|
||||
children.push(f.hash);
|
||||
folders.insert(f.hash, f);
|
||||
mailboxes.insert(f.hash, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -755,55 +755,55 @@ impl MaildirType {
|
|||
}
|
||||
Ok(children)
|
||||
};
|
||||
let root_path = PathBuf::from(settings.root_folder()).expand();
|
||||
let root_path = PathBuf::from(settings.root_mailbox()).expand();
|
||||
if !root_path.exists() {
|
||||
return Err(MeliError::new(format!(
|
||||
"Configuration error ({}): root_path `{}` is not a valid directory.",
|
||||
settings.name(),
|
||||
settings.root_folder.as_str()
|
||||
settings.root_mailbox.as_str()
|
||||
)));
|
||||
} else if !root_path.is_dir() {
|
||||
return Err(MeliError::new(format!(
|
||||
"Configuration error ({}): root_path `{}` is not a directory.",
|
||||
settings.name(),
|
||||
settings.root_folder.as_str()
|
||||
settings.root_mailbox.as_str()
|
||||
)));
|
||||
}
|
||||
|
||||
if let Ok(f) = MaildirFolder::new(
|
||||
if let Ok(f) = MaildirMailbox::new(
|
||||
root_path.to_str().unwrap().to_string(),
|
||||
root_path.file_name().unwrap().to_str().unwrap().to_string(),
|
||||
None,
|
||||
Vec::with_capacity(0),
|
||||
settings,
|
||||
) {
|
||||
folders.insert(f.hash, f);
|
||||
mailboxes.insert(f.hash, f);
|
||||
}
|
||||
|
||||
if folders.is_empty() {
|
||||
let children = recurse_folders(&mut folders, settings, &root_path)?;
|
||||
if mailboxes.is_empty() {
|
||||
let children = recurse_mailboxes(&mut mailboxes, settings, &root_path)?;
|
||||
children
|
||||
.iter()
|
||||
.map(|c| folders.get_mut(c).map(|f| f.parent = None))
|
||||
.map(|c| mailboxes.get_mut(c).map(|f| f.parent = None))
|
||||
.count();
|
||||
} else {
|
||||
let root_hash = *folders.keys().nth(0).unwrap();
|
||||
let children = recurse_folders(&mut folders, settings, &root_path)?;
|
||||
let root_hash = *mailboxes.keys().nth(0).unwrap();
|
||||
let children = recurse_mailboxes(&mut mailboxes, settings, &root_path)?;
|
||||
children
|
||||
.iter()
|
||||
.map(|c| folders.get_mut(c).map(|f| f.parent = Some(root_hash)))
|
||||
.map(|c| mailboxes.get_mut(c).map(|f| f.parent = Some(root_hash)))
|
||||
.count();
|
||||
folders.get_mut(&root_hash).map(|f| f.children = children);
|
||||
mailboxes.get_mut(&root_hash).map(|f| f.children = children);
|
||||
}
|
||||
for f in folders.values_mut() {
|
||||
for f in mailboxes.values_mut() {
|
||||
if is_subscribed(f.path()) {
|
||||
f.is_subscribed = true;
|
||||
}
|
||||
}
|
||||
|
||||
let mut hash_indexes =
|
||||
FnvHashMap::with_capacity_and_hasher(folders.len(), Default::default());
|
||||
for &fh in folders.keys() {
|
||||
FnvHashMap::with_capacity_and_hasher(mailboxes.len(), Default::default());
|
||||
for &fh in mailboxes.keys() {
|
||||
hash_indexes.insert(
|
||||
fh,
|
||||
HashIndex {
|
||||
|
@ -814,37 +814,37 @@ impl MaildirType {
|
|||
}
|
||||
Ok(Box::new(MaildirType {
|
||||
name: settings.name().to_string(),
|
||||
folders,
|
||||
mailboxes,
|
||||
hash_indexes: Arc::new(Mutex::new(hash_indexes)),
|
||||
folder_index: Default::default(),
|
||||
mailbox_index: Default::default(),
|
||||
path: root_path,
|
||||
}))
|
||||
}
|
||||
fn owned_folder_idx(&self, folder: &Folder) -> FolderHash {
|
||||
fn owned_mailbox_idx(&self, mailbox: &Mailbox) -> MailboxHash {
|
||||
*self
|
||||
.folders
|
||||
.mailboxes
|
||||
.iter()
|
||||
.find(|(_, f)| f.hash() == folder.hash())
|
||||
.find(|(_, f)| f.hash() == mailbox.hash())
|
||||
.unwrap()
|
||||
.0
|
||||
}
|
||||
|
||||
pub fn multicore(&mut self, cores: usize, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||
pub fn multicore(&mut self, cores: usize, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
|
||||
let mut w = AsyncBuilder::new();
|
||||
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
|
||||
|
||||
let handle = {
|
||||
let tx = w.tx();
|
||||
let folder: &MaildirFolder = &self.folders[&self.owned_folder_idx(folder)];
|
||||
let folder_hash = folder.hash();
|
||||
let unseen = folder.unseen.clone();
|
||||
let total = folder.total.clone();
|
||||
let mailbox: &MaildirMailbox = &self.mailboxes[&self.owned_mailbox_idx(mailbox)];
|
||||
let mailbox_hash = mailbox.hash();
|
||||
let unseen = mailbox.unseen.clone();
|
||||
let total = mailbox.total.clone();
|
||||
let tx_final = w.tx();
|
||||
let mut path: PathBuf = folder.fs_path().into();
|
||||
let name = format!("parsing {:?}", folder.name());
|
||||
let mut path: PathBuf = mailbox.fs_path().into();
|
||||
let name = format!("parsing {:?}", mailbox.name());
|
||||
let root_path = self.path.to_path_buf();
|
||||
let map = self.hash_indexes.clone();
|
||||
let folder_index = self.folder_index.clone();
|
||||
let mailbox_index = self.mailbox_index.clone();
|
||||
|
||||
let closure = move |work_context: crate::async_workers::WorkContext| {
|
||||
work_context
|
||||
|
@ -886,7 +886,7 @@ impl MaildirType {
|
|||
let cache_dir = cache_dir.clone();
|
||||
let tx = tx.clone();
|
||||
let map = map.clone();
|
||||
let folder_index = folder_index.clone();
|
||||
let mailbox_index = mailbox_index.clone();
|
||||
let root_path = root_path.clone();
|
||||
let s = scope.builder().name(name.clone()).spawn(move |_| {
|
||||
let len = chunk.len();
|
||||
|
@ -896,7 +896,7 @@ impl MaildirType {
|
|||
for c in chunk.chunks(size) {
|
||||
//thread::yield_now();
|
||||
let map = map.clone();
|
||||
let folder_index = folder_index.clone();
|
||||
let mailbox_index = mailbox_index.clone();
|
||||
let len = c.len();
|
||||
for file in c {
|
||||
/* Check if we have a cache file with this email's
|
||||
|
@ -916,13 +916,13 @@ impl MaildirType {
|
|||
bincode::deserialize_from(reader);
|
||||
if let Ok(env) = result {
|
||||
let mut map = map.lock().unwrap();
|
||||
let map = map.entry(folder_hash).or_default();
|
||||
let map = map.entry(mailbox_hash).or_default();
|
||||
let hash = env.hash();
|
||||
map.insert(hash, file.clone().into());
|
||||
folder_index
|
||||
mailbox_index
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(hash, folder_hash);
|
||||
.insert(hash, mailbox_hash);
|
||||
if !env.is_seen() {
|
||||
*unseen.lock().unwrap() += 1;
|
||||
}
|
||||
|
@ -934,19 +934,19 @@ impl MaildirType {
|
|||
let hash = get_file_hash(file);
|
||||
{
|
||||
let mut map = map.lock().unwrap();
|
||||
let map = map.entry(folder_hash).or_default();
|
||||
let map = map.entry(mailbox_hash).or_default();
|
||||
(*map).insert(hash, PathBuf::from(file).into());
|
||||
}
|
||||
let op = Box::new(MaildirOp::new(
|
||||
hash,
|
||||
map.clone(),
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
));
|
||||
if let Some(e) = Envelope::from_token(op, hash) {
|
||||
folder_index
|
||||
mailbox_index
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(e.hash(), folder_hash);
|
||||
.insert(e.hash(), mailbox_hash);
|
||||
if let Ok(cached) =
|
||||
cache_dir.place_cache_file(file_name)
|
||||
{
|
||||
|
@ -1011,7 +1011,7 @@ impl MaildirType {
|
|||
w.build(handle)
|
||||
}
|
||||
|
||||
pub fn save_to_folder(mut path: PathBuf, bytes: &[u8], flags: Option<Flag>) -> Result<()> {
|
||||
pub fn save_to_mailbox(mut path: PathBuf, bytes: &[u8], flags: Option<Flag>) -> Result<()> {
|
||||
path.push("cur");
|
||||
{
|
||||
let mut rand_buf = [0u8; 16];
|
||||
|
@ -1071,18 +1071,18 @@ impl MaildirType {
|
|||
}
|
||||
|
||||
pub fn validate_config(s: &AccountSettings) -> Result<()> {
|
||||
let root_path = PathBuf::from(s.root_folder()).expand();
|
||||
let root_path = PathBuf::from(s.root_mailbox()).expand();
|
||||
if !root_path.exists() {
|
||||
return Err(MeliError::new(format!(
|
||||
"Configuration error ({}): root_path `{}` is not a valid directory.",
|
||||
s.name(),
|
||||
s.root_folder.as_str()
|
||||
s.root_mailbox.as_str()
|
||||
)));
|
||||
} else if !root_path.is_dir() {
|
||||
return Err(MeliError::new(format!(
|
||||
"Configuration error ({}): root_path `{}` is not a directory.",
|
||||
s.name(),
|
||||
s.root_folder.as_str()
|
||||
s.root_mailbox.as_str()
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -1092,7 +1092,7 @@ impl MaildirType {
|
|||
|
||||
fn add_path_to_index(
|
||||
hash_index: &HashIndexes,
|
||||
folder_hash: FolderHash,
|
||||
mailbox_hash: MailboxHash,
|
||||
path: &Path,
|
||||
cache_dir: &xdg::BaseDirectories,
|
||||
file_name: PathBuf,
|
||||
|
@ -1102,16 +1102,16 @@ fn add_path_to_index(
|
|||
let hash = get_file_hash(path);
|
||||
{
|
||||
let mut map = hash_index.lock().unwrap();
|
||||
let map = map.entry(folder_hash).or_default();
|
||||
let map = map.entry(mailbox_hash).or_default();
|
||||
map.insert(hash, path.to_path_buf().into());
|
||||
debug!(
|
||||
"inserted {} in {} map, len={}",
|
||||
hash,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
map.len()
|
||||
);
|
||||
}
|
||||
let op = Box::new(MaildirOp::new(hash, hash_index.clone(), folder_hash));
|
||||
let op = Box::new(MaildirOp::new(hash, hash_index.clone(), mailbox_hash));
|
||||
if let Some(e) = Envelope::from_token(op, hash) {
|
||||
debug!("add_path_to_index gen {}\t{}", hash, file_name.display());
|
||||
if let Ok(cached) = cache_dir.place_cache_file(file_name) {
|
||||
|
|
|
@ -25,9 +25,9 @@
|
|||
|
||||
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
||||
use crate::backends::BackendOp;
|
||||
use crate::backends::FolderHash;
|
||||
use crate::backends::MailboxHash;
|
||||
use crate::backends::{
|
||||
BackendFolder, Folder, FolderPermissions, MailBackend, RefreshEvent, RefreshEventConsumer,
|
||||
BackendMailbox, MailBackend, Mailbox, MailboxPermissions, RefreshEvent, RefreshEventConsumer,
|
||||
RefreshEventKind, SpecialUsageMailbox,
|
||||
};
|
||||
use crate::conf::AccountSettings;
|
||||
|
@ -74,22 +74,22 @@ fn get_rw_lock_blocking(f: &File) {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MboxFolder {
|
||||
hash: FolderHash,
|
||||
struct MboxMailbox {
|
||||
hash: MailboxHash,
|
||||
name: String,
|
||||
path: PathBuf,
|
||||
content: Vec<u8>,
|
||||
children: Vec<FolderHash>,
|
||||
parent: Option<FolderHash>,
|
||||
children: Vec<MailboxHash>,
|
||||
parent: Option<MailboxHash>,
|
||||
usage: Arc<RwLock<SpecialUsageMailbox>>,
|
||||
is_subscribed: bool,
|
||||
permissions: FolderPermissions,
|
||||
permissions: MailboxPermissions,
|
||||
pub total: Arc<Mutex<usize>>,
|
||||
pub unseen: Arc<Mutex<usize>>,
|
||||
}
|
||||
|
||||
impl BackendFolder for MboxFolder {
|
||||
fn hash(&self) -> FolderHash {
|
||||
impl BackendMailbox for MboxMailbox {
|
||||
fn hash(&self) -> MailboxHash {
|
||||
self.hash
|
||||
}
|
||||
|
||||
|
@ -106,8 +106,8 @@ impl BackendFolder for MboxFolder {
|
|||
self.name = s.to_string();
|
||||
}
|
||||
|
||||
fn clone(&self) -> Folder {
|
||||
Box::new(MboxFolder {
|
||||
fn clone(&self) -> Mailbox {
|
||||
Box::new(MboxMailbox {
|
||||
hash: self.hash,
|
||||
name: self.name.clone(),
|
||||
path: self.path.clone(),
|
||||
|
@ -122,11 +122,11 @@ impl BackendFolder for MboxFolder {
|
|||
})
|
||||
}
|
||||
|
||||
fn children(&self) -> &[FolderHash] {
|
||||
fn children(&self) -> &[MailboxHash] {
|
||||
&self.children
|
||||
}
|
||||
|
||||
fn parent(&self) -> Option<FolderHash> {
|
||||
fn parent(&self) -> Option<MailboxHash> {
|
||||
self.parent
|
||||
}
|
||||
|
||||
|
@ -134,7 +134,7 @@ impl BackendFolder for MboxFolder {
|
|||
*self.usage.read().unwrap()
|
||||
}
|
||||
|
||||
fn permissions(&self) -> FolderPermissions {
|
||||
fn permissions(&self) -> MailboxPermissions {
|
||||
self.permissions
|
||||
}
|
||||
fn is_subscribed(&self) -> bool {
|
||||
|
@ -388,28 +388,28 @@ pub fn mbox_parse(
|
|||
pub struct MboxType {
|
||||
path: PathBuf,
|
||||
index: Arc<Mutex<FnvHashMap<EnvelopeHash, (Offset, Length)>>>,
|
||||
folders: Arc<Mutex<FnvHashMap<FolderHash, MboxFolder>>>,
|
||||
mailboxes: Arc<Mutex<FnvHashMap<MailboxHash, MboxMailbox>>>,
|
||||
}
|
||||
|
||||
impl MailBackend for MboxType {
|
||||
fn is_online(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||
fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
|
||||
let mut w = AsyncBuilder::new();
|
||||
let handle = {
|
||||
let tx = w.tx();
|
||||
let index = self.index.clone();
|
||||
let folder_path = folder.path().to_string();
|
||||
let folder_hash = folder.hash();
|
||||
let folders = self.folders.clone();
|
||||
let mailbox_path = mailbox.path().to_string();
|
||||
let mailbox_hash = mailbox.hash();
|
||||
let mailboxes = self.mailboxes.clone();
|
||||
let closure = move |_work_context| {
|
||||
let tx = tx.clone();
|
||||
let index = index.clone();
|
||||
let file = match std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.open(&folder_path)
|
||||
.open(&mailbox_path)
|
||||
{
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
|
@ -431,9 +431,9 @@ impl MailBackend for MboxType {
|
|||
.to_full_result()
|
||||
.map_err(|e| MeliError::from(e));
|
||||
{
|
||||
let mut folder_lock = folders.lock().unwrap();
|
||||
folder_lock
|
||||
.entry(folder_hash)
|
||||
let mut mailbox_lock = mailboxes.lock().unwrap();
|
||||
mailbox_lock
|
||||
.entry(mailbox_hash)
|
||||
.and_modify(|f| f.content = contents);
|
||||
}
|
||||
|
||||
|
@ -454,7 +454,7 @@ impl MailBackend for MboxType {
|
|||
let mut watcher = watcher(tx, std::time::Duration::from_secs(10))
|
||||
.map_err(|e| e.to_string())
|
||||
.map_err(MeliError::new)?;
|
||||
for f in self.folders.lock().unwrap().values() {
|
||||
for f in self.mailboxes.lock().unwrap().values() {
|
||||
watcher
|
||||
.watch(&f.path, RecursiveMode::Recursive)
|
||||
.map_err(|e| e.to_string())
|
||||
|
@ -462,7 +462,7 @@ impl MailBackend for MboxType {
|
|||
debug!("watching {:?}", f.path.as_path());
|
||||
}
|
||||
let index = self.index.clone();
|
||||
let folders = self.folders.clone();
|
||||
let mailboxes = self.mailboxes.clone();
|
||||
let handle = std::thread::Builder::new()
|
||||
.name(format!(
|
||||
"watching {}",
|
||||
|
@ -473,7 +473,7 @@ impl MailBackend for MboxType {
|
|||
let _watcher = watcher;
|
||||
let _work_context = work_context;
|
||||
let index = index;
|
||||
let folders = folders;
|
||||
let mailboxes = mailboxes;
|
||||
loop {
|
||||
match rx.recv() {
|
||||
/*
|
||||
|
@ -490,7 +490,7 @@ impl MailBackend for MboxType {
|
|||
/* Update */
|
||||
DebouncedEvent::NoticeWrite(pathbuf)
|
||||
| DebouncedEvent::Write(pathbuf) => {
|
||||
let folder_hash = get_path_hash!(&pathbuf);
|
||||
let mailbox_hash = get_path_hash!(&pathbuf);
|
||||
let file = match std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
|
@ -502,7 +502,7 @@ impl MailBackend for MboxType {
|
|||
}
|
||||
};
|
||||
get_rw_lock_blocking(&file);
|
||||
let mut folder_lock = folders.lock().unwrap();
|
||||
let mut mailbox_lock = mailboxes.lock().unwrap();
|
||||
let mut buf_reader = BufReader::new(file);
|
||||
let mut contents = Vec::new();
|
||||
if let Err(e) = buf_reader.read_to_end(&mut contents) {
|
||||
|
@ -510,46 +510,46 @@ impl MailBackend for MboxType {
|
|||
continue;
|
||||
};
|
||||
if contents
|
||||
.starts_with(folder_lock[&folder_hash].content.as_slice())
|
||||
.starts_with(mailbox_lock[&mailbox_hash].content.as_slice())
|
||||
{
|
||||
if let Ok(envelopes) = mbox_parse(
|
||||
index.clone(),
|
||||
&contents[folder_lock[&folder_hash].content.len()..],
|
||||
folder_lock[&folder_hash].content.len(),
|
||||
&contents[mailbox_lock[&mailbox_hash].content.len()..],
|
||||
mailbox_lock[&mailbox_hash].content.len(),
|
||||
)
|
||||
.to_full_result()
|
||||
{
|
||||
for env in envelopes {
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: RefreshEventKind::Create(Box::new(env)),
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: RefreshEventKind::Rescan,
|
||||
});
|
||||
}
|
||||
folder_lock
|
||||
.entry(folder_hash)
|
||||
mailbox_lock
|
||||
.entry(mailbox_hash)
|
||||
.and_modify(|f| f.content = contents);
|
||||
}
|
||||
/* Remove */
|
||||
DebouncedEvent::NoticeRemove(pathbuf)
|
||||
| DebouncedEvent::Remove(pathbuf) => {
|
||||
if folders
|
||||
if mailboxes
|
||||
.lock()
|
||||
.unwrap()
|
||||
.values()
|
||||
.any(|f| &f.path == &pathbuf)
|
||||
{
|
||||
let folder_hash = get_path_hash!(&pathbuf);
|
||||
let mailbox_hash = get_path_hash!(&pathbuf);
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
||||
"mbox folder {} was removed.",
|
||||
"mbox mailbox {} was removed.",
|
||||
pathbuf.display()
|
||||
))),
|
||||
});
|
||||
|
@ -557,12 +557,12 @@ impl MailBackend for MboxType {
|
|||
}
|
||||
}
|
||||
DebouncedEvent::Rename(src, dest) => {
|
||||
if folders.lock().unwrap().values().any(|f| &f.path == &src) {
|
||||
let folder_hash = get_path_hash!(&src);
|
||||
if mailboxes.lock().unwrap().values().any(|f| &f.path == &src) {
|
||||
let mailbox_hash = get_path_hash!(&src);
|
||||
sender.send(RefreshEvent {
|
||||
hash: folder_hash,
|
||||
hash: mailbox_hash,
|
||||
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
||||
"mbox folder {} was renamed to {}.",
|
||||
"mbox mailbox {} was renamed to {}.",
|
||||
src.display(),
|
||||
dest.display()
|
||||
))),
|
||||
|
@ -570,9 +570,9 @@ impl MailBackend for MboxType {
|
|||
return;
|
||||
}
|
||||
}
|
||||
/* Trigger rescan of folders */
|
||||
/* Trigger rescan of mailboxes */
|
||||
DebouncedEvent::Rescan => {
|
||||
for h in folders.lock().unwrap().keys() {
|
||||
for h in mailboxes.lock().unwrap().keys() {
|
||||
sender.send(RefreshEvent {
|
||||
hash: *h,
|
||||
kind: RefreshEventKind::Rescan,
|
||||
|
@ -588,13 +588,13 @@ impl MailBackend for MboxType {
|
|||
})?;
|
||||
Ok(handle.thread().id())
|
||||
}
|
||||
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
|
||||
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||
Ok(self
|
||||
.folders
|
||||
.mailboxes
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|(h, f)| (*h, f.clone() as Folder))
|
||||
.map(|(h, f)| (*h, f.clone() as Mailbox))
|
||||
.collect())
|
||||
}
|
||||
fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp> {
|
||||
|
@ -605,7 +605,7 @@ impl MailBackend for MboxType {
|
|||
Box::new(MboxOp::new(hash, self.path.as_path(), offset, length))
|
||||
}
|
||||
|
||||
fn save(&self, _bytes: &[u8], _folder: &str, _flags: Option<Flag>) -> Result<()> {
|
||||
fn save(&self, _bytes: &[u8], _mailbox: &str, _flags: Option<Flag>) -> Result<()> {
|
||||
Err(MeliError::new("Unimplemented."))
|
||||
}
|
||||
|
||||
|
@ -619,11 +619,11 @@ impl MboxType {
|
|||
s: &AccountSettings,
|
||||
_is_subscribed: Box<dyn Fn(&str) -> bool>,
|
||||
) -> Result<Box<dyn MailBackend>> {
|
||||
let path = Path::new(s.root_folder.as_str()).expand();
|
||||
let path = Path::new(s.root_mailbox.as_str()).expand();
|
||||
if !path.exists() {
|
||||
return Err(MeliError::new(format!(
|
||||
"\"root_folder\" {} for account {} is not a valid path.",
|
||||
s.root_folder.as_str(),
|
||||
"\"root_mailbox\" {} for account {} is not a valid path.",
|
||||
s.root_mailbox.as_str(),
|
||||
s.name()
|
||||
)));
|
||||
}
|
||||
|
@ -644,9 +644,9 @@ impl MboxType {
|
|||
true
|
||||
};
|
||||
|
||||
ret.folders.lock().unwrap().insert(
|
||||
ret.mailboxes.lock().unwrap().insert(
|
||||
hash,
|
||||
MboxFolder {
|
||||
MboxMailbox {
|
||||
hash,
|
||||
path: ret.path.clone(),
|
||||
name,
|
||||
|
@ -655,7 +655,7 @@ impl MboxType {
|
|||
parent: None,
|
||||
usage: Arc::new(RwLock::new(SpecialUsageMailbox::Normal)),
|
||||
is_subscribed: true,
|
||||
permissions: FolderPermissions {
|
||||
permissions: MailboxPermissions {
|
||||
create_messages: !read_only,
|
||||
remove_messages: !read_only,
|
||||
set_flags: !read_only,
|
||||
|
@ -671,8 +671,8 @@ impl MboxType {
|
|||
);
|
||||
/*
|
||||
/* Look for other mailboxes */
|
||||
let parent_folder = Path::new(path).parent().unwrap();
|
||||
let read_dir = std::fs::read_dir(parent_folder);
|
||||
let parent_mailbox = Path::new(path).parent().unwrap();
|
||||
let read_dir = std::fs::read_dir(parent_mailbox);
|
||||
if read_dir.is_ok() {
|
||||
for f in read_dir.unwrap() {
|
||||
if f.is_err() {
|
||||
|
@ -685,9 +685,9 @@ impl MboxType {
|
|||
.map(|f| f.to_string_lossy().into())
|
||||
.unwrap_or(String::new());
|
||||
let hash = get_path_hash!(f);
|
||||
ret.folders.lock().unwrap().insert(
|
||||
ret.mailboxes.lock().unwrap().insert(
|
||||
hash,
|
||||
MboxFolder {
|
||||
MboxMailbox {
|
||||
hash,
|
||||
path: f,
|
||||
name,
|
||||
|
@ -704,11 +704,11 @@ impl MboxType {
|
|||
}
|
||||
|
||||
pub fn validate_config(s: &AccountSettings) -> Result<()> {
|
||||
let path = Path::new(s.root_folder.as_str()).expand();
|
||||
let path = Path::new(s.root_mailbox.as_str()).expand();
|
||||
if !path.exists() {
|
||||
return Err(MeliError::new(format!(
|
||||
"\"root_folder\" {} for account {} is not a valid path.",
|
||||
s.root_folder.as_str(),
|
||||
"\"root_mailbox\" {} for account {} is not a valid path.",
|
||||
s.root_mailbox.as_str(),
|
||||
s.name()
|
||||
)));
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
*/
|
||||
|
||||
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
||||
use crate::backends::FolderHash;
|
||||
use crate::backends::MailboxHash;
|
||||
use crate::backends::{
|
||||
BackendFolder, BackendOp, Folder, FolderPermissions, MailBackend, RefreshEventConsumer,
|
||||
BackendMailbox, BackendOp, MailBackend, Mailbox, MailboxPermissions, RefreshEventConsumer,
|
||||
SpecialUsageMailbox,
|
||||
};
|
||||
use crate::conf::AccountSettings;
|
||||
|
@ -54,7 +54,7 @@ unsafe impl Sync for DbWrapper {}
|
|||
#[derive(Debug)]
|
||||
pub struct NotmuchDb {
|
||||
database: DbWrapper,
|
||||
folders: Arc<RwLock<FnvHashMap<FolderHash, NotmuchFolder>>>,
|
||||
mailboxes: Arc<RwLock<FnvHashMap<MailboxHash, NotmuchMailbox>>>,
|
||||
index: Arc<RwLock<FnvHashMap<EnvelopeHash, &'static CStr>>>,
|
||||
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||
path: PathBuf,
|
||||
|
@ -66,7 +66,7 @@ unsafe impl Sync for NotmuchDb {}
|
|||
|
||||
impl Drop for NotmuchDb {
|
||||
fn drop(&mut self) {
|
||||
for f in self.folders.write().unwrap().values_mut() {
|
||||
for f in self.mailboxes.write().unwrap().values_mut() {
|
||||
if let Some(query) = f.query.take() {
|
||||
unsafe {
|
||||
notmuch_query_destroy(query);
|
||||
|
@ -82,10 +82,10 @@ impl Drop for NotmuchDb {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
struct NotmuchFolder {
|
||||
hash: FolderHash,
|
||||
children: Vec<FolderHash>,
|
||||
parent: Option<FolderHash>,
|
||||
struct NotmuchMailbox {
|
||||
hash: MailboxHash,
|
||||
children: Vec<MailboxHash>,
|
||||
parent: Option<MailboxHash>,
|
||||
name: String,
|
||||
path: String,
|
||||
query_str: String,
|
||||
|
@ -97,8 +97,8 @@ struct NotmuchFolder {
|
|||
unseen: Arc<Mutex<usize>>,
|
||||
}
|
||||
|
||||
impl BackendFolder for NotmuchFolder {
|
||||
fn hash(&self) -> FolderHash {
|
||||
impl BackendMailbox for NotmuchMailbox {
|
||||
fn hash(&self) -> MailboxHash {
|
||||
self.hash
|
||||
}
|
||||
|
||||
|
@ -112,15 +112,15 @@ impl BackendFolder for NotmuchFolder {
|
|||
|
||||
fn change_name(&mut self, _s: &str) {}
|
||||
|
||||
fn clone(&self) -> Folder {
|
||||
fn clone(&self) -> Mailbox {
|
||||
Box::new(std::clone::Clone::clone(self))
|
||||
}
|
||||
|
||||
fn children(&self) -> &[FolderHash] {
|
||||
fn children(&self) -> &[MailboxHash] {
|
||||
&self.children
|
||||
}
|
||||
|
||||
fn parent(&self) -> Option<FolderHash> {
|
||||
fn parent(&self) -> Option<MailboxHash> {
|
||||
self.parent
|
||||
}
|
||||
|
||||
|
@ -128,8 +128,8 @@ impl BackendFolder for NotmuchFolder {
|
|||
*self.usage.read().unwrap()
|
||||
}
|
||||
|
||||
fn permissions(&self) -> FolderPermissions {
|
||||
FolderPermissions::default()
|
||||
fn permissions(&self) -> MailboxPermissions {
|
||||
MailboxPermissions::default()
|
||||
}
|
||||
|
||||
fn is_subscribed(&self) -> bool {
|
||||
|
@ -150,8 +150,8 @@ impl BackendFolder for NotmuchFolder {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for NotmuchFolder {}
|
||||
unsafe impl Sync for NotmuchFolder {}
|
||||
unsafe impl Send for NotmuchMailbox {}
|
||||
unsafe impl Sync for NotmuchMailbox {}
|
||||
|
||||
impl NotmuchDb {
|
||||
pub fn new(
|
||||
|
@ -159,11 +159,11 @@ impl NotmuchDb {
|
|||
_is_subscribed: Box<dyn Fn(&str) -> bool>,
|
||||
) -> Result<Box<dyn MailBackend>> {
|
||||
let mut database: *mut notmuch_database_t = std::ptr::null_mut();
|
||||
let path = Path::new(s.root_folder.as_str()).expand().to_path_buf();
|
||||
let path = Path::new(s.root_mailbox.as_str()).expand().to_path_buf();
|
||||
if !path.exists() {
|
||||
return Err(MeliError::new(format!(
|
||||
"\"root_folder\" {} for account {} is not a valid path.",
|
||||
s.root_folder.as_str(),
|
||||
"\"root_mailbox\" {} for account {} is not a valid path.",
|
||||
s.root_mailbox.as_str(),
|
||||
s.name()
|
||||
)));
|
||||
}
|
||||
|
@ -180,22 +180,22 @@ impl NotmuchDb {
|
|||
if status != 0 {
|
||||
return Err(MeliError::new(format!(
|
||||
"Could not open notmuch database at path {}. notmuch_database_open returned {}.",
|
||||
s.root_folder.as_str(),
|
||||
s.root_mailbox.as_str(),
|
||||
status
|
||||
)));
|
||||
}
|
||||
assert!(!database.is_null());
|
||||
let mut folders = FnvHashMap::default();
|
||||
for (k, f) in s.folders.iter() {
|
||||
let mut mailboxes = FnvHashMap::default();
|
||||
for (k, f) in s.mailboxes.iter() {
|
||||
if let Some(query_str) = f.extra.get("query") {
|
||||
let hash = {
|
||||
let mut h = DefaultHasher::new();
|
||||
k.hash(&mut h);
|
||||
h.finish()
|
||||
};
|
||||
folders.insert(
|
||||
mailboxes.insert(
|
||||
hash,
|
||||
NotmuchFolder {
|
||||
NotmuchMailbox {
|
||||
hash,
|
||||
name: k.to_string(),
|
||||
path: k.to_string(),
|
||||
|
@ -211,7 +211,7 @@ impl NotmuchDb {
|
|||
);
|
||||
} else {
|
||||
return Err(MeliError::new(format!(
|
||||
"notmuch folder configuration entry \"{}\" should have a \"query\" value set.",
|
||||
"notmuch mailbox configuration entry \"{}\" should have a \"query\" value set.",
|
||||
k
|
||||
)));
|
||||
}
|
||||
|
@ -225,24 +225,24 @@ impl NotmuchDb {
|
|||
index: Arc::new(RwLock::new(Default::default())),
|
||||
tag_index: Arc::new(RwLock::new(Default::default())),
|
||||
|
||||
folders: Arc::new(RwLock::new(folders)),
|
||||
mailboxes: Arc::new(RwLock::new(mailboxes)),
|
||||
save_messages_to: None,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn validate_config(s: &AccountSettings) -> Result<()> {
|
||||
let path = Path::new(s.root_folder.as_str()).expand().to_path_buf();
|
||||
let path = Path::new(s.root_mailbox.as_str()).expand().to_path_buf();
|
||||
if !path.exists() {
|
||||
return Err(MeliError::new(format!(
|
||||
"\"root_folder\" {} for account {} is not a valid path.",
|
||||
s.root_folder.as_str(),
|
||||
"\"root_mailbox\" {} for account {} is not a valid path.",
|
||||
s.root_mailbox.as_str(),
|
||||
s.name()
|
||||
)));
|
||||
}
|
||||
for (k, f) in s.folders.iter() {
|
||||
for (k, f) in s.mailboxes.iter() {
|
||||
if f.extra.get("query").is_none() {
|
||||
return Err(MeliError::new(format!(
|
||||
"notmuch folder configuration entry \"{}\" should have a \"query\" value set.",
|
||||
"notmuch mailbox configuration entry \"{}\" should have a \"query\" value set.",
|
||||
k
|
||||
)));
|
||||
}
|
||||
|
@ -288,21 +288,21 @@ impl MailBackend for NotmuchDb {
|
|||
fn is_online(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||
fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
|
||||
let mut w = AsyncBuilder::new();
|
||||
let folder_hash = folder.hash();
|
||||
let mailbox_hash = mailbox.hash();
|
||||
let database = self.database.clone();
|
||||
let index = self.index.clone();
|
||||
let tag_index = self.tag_index.clone();
|
||||
let folders = self.folders.clone();
|
||||
let mailboxes = self.mailboxes.clone();
|
||||
let handle = {
|
||||
let tx = w.tx();
|
||||
let closure = move |_work_context| {
|
||||
let mut ret: Vec<Envelope> = Vec::new();
|
||||
let database_lck = database.inner.read().unwrap();
|
||||
let mut folders_lck = folders.write().unwrap();
|
||||
let folder = folders_lck.get_mut(&folder_hash).unwrap();
|
||||
let query_str = std::ffi::CString::new(folder.query_str.as_str()).unwrap();
|
||||
let mut mailboxes_lck = mailboxes.write().unwrap();
|
||||
let mailbox = mailboxes_lck.get_mut(&mailbox_hash).unwrap();
|
||||
let query_str = std::ffi::CString::new(mailbox.query_str.as_str()).unwrap();
|
||||
let query: *mut notmuch_query_t =
|
||||
unsafe { notmuch_query_create(*database_lck, query_str.as_ptr()) };
|
||||
if query.is_null() {
|
||||
|
@ -319,7 +319,7 @@ impl MailBackend for NotmuchDb {
|
|||
if status != 0 {
|
||||
tx.send(AsyncStatus::Payload(Err(MeliError::new(format!(
|
||||
"Search for {} returned {}",
|
||||
folder.query_str.as_str(),
|
||||
mailbox.query_str.as_str(),
|
||||
status,
|
||||
)))))
|
||||
.unwrap();
|
||||
|
@ -383,7 +383,7 @@ impl MailBackend for NotmuchDb {
|
|||
index.write().unwrap().remove(&env_hash);
|
||||
}
|
||||
}
|
||||
folder.query = Some(query);
|
||||
mailbox.query = Some(query);
|
||||
tx.send(AsyncStatus::Payload(Ok(ret))).unwrap();
|
||||
tx.send(AsyncStatus::Finished).unwrap();
|
||||
};
|
||||
|
@ -405,13 +405,13 @@ impl MailBackend for NotmuchDb {
|
|||
.spawn(move || {})?;
|
||||
Ok(handle.thread().id())
|
||||
}
|
||||
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
|
||||
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||
Ok(self
|
||||
.folders
|
||||
.mailboxes
|
||||
.read()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(|(k, f)| (*k, BackendFolder::clone(f)))
|
||||
.map(|(k, f)| (*k, BackendMailbox::clone(f)))
|
||||
.collect())
|
||||
}
|
||||
fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp> {
|
||||
|
@ -424,7 +424,7 @@ impl MailBackend for NotmuchDb {
|
|||
})
|
||||
}
|
||||
|
||||
fn save(&self, bytes: &[u8], _folder: &str, flags: Option<Flag>) -> Result<()> {
|
||||
fn save(&self, bytes: &[u8], _mailbox: &str, flags: Option<Flag>) -> Result<()> {
|
||||
let mut path = self
|
||||
.save_messages_to
|
||||
.as_ref()
|
||||
|
@ -435,7 +435,7 @@ impl MailBackend for NotmuchDb {
|
|||
path.push(d);
|
||||
if !path.is_dir() {
|
||||
return Err(MeliError::new(format!(
|
||||
"{} is not a valid maildir folder",
|
||||
"{} is not a valid maildir mailbox",
|
||||
path.display()
|
||||
)));
|
||||
}
|
||||
|
@ -443,7 +443,7 @@ impl MailBackend for NotmuchDb {
|
|||
}
|
||||
path.push("cur");
|
||||
}
|
||||
crate::backends::MaildirType::save_to_folder(path, bytes, flags)
|
||||
crate::backends::MaildirType::save_to_mailbox(path, bytes, flags)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn::std::any::Any {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
*/
|
||||
|
||||
use super::*;
|
||||
use crate::backends::FolderHash;
|
||||
use crate::backends::MailboxHash;
|
||||
use core::ops::{Index, IndexMut};
|
||||
use smallvec::SmallVec;
|
||||
use std::collections::BTreeMap;
|
||||
|
@ -67,9 +67,9 @@ pub struct Collection {
|
|||
message_ids: FnvHashMap<Vec<u8>, EnvelopeHash>,
|
||||
date_index: BTreeMap<UnixTimestamp, EnvelopeHash>,
|
||||
subject_index: Option<BTreeMap<String, EnvelopeHash>>,
|
||||
pub threads: FnvHashMap<FolderHash, Threads>,
|
||||
sent_folder: Option<FolderHash>,
|
||||
pub mailboxes: FnvHashMap<FolderHash, FnvHashSet<EnvelopeHash>>,
|
||||
pub threads: FnvHashMap<MailboxHash, Threads>,
|
||||
sent_mailbox: Option<MailboxHash>,
|
||||
pub mailboxes: FnvHashMap<MailboxHash, FnvHashSet<EnvelopeHash>>,
|
||||
}
|
||||
|
||||
impl Drop for Collection {
|
||||
|
@ -112,7 +112,7 @@ impl Collection {
|
|||
subject_index,
|
||||
threads,
|
||||
mailboxes,
|
||||
sent_folder: None,
|
||||
sent_mailbox: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,18 +124,18 @@ impl Collection {
|
|||
self.envelopes.read().unwrap().is_empty()
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, envelope_hash: EnvelopeHash, folder_hash: FolderHash) {
|
||||
pub fn remove(&mut self, envelope_hash: EnvelopeHash, mailbox_hash: MailboxHash) {
|
||||
debug!("DEBUG: Removing {}", envelope_hash);
|
||||
self.envelopes.write().unwrap().remove(&envelope_hash);
|
||||
self.mailboxes.entry(folder_hash).and_modify(|m| {
|
||||
self.mailboxes.entry(mailbox_hash).and_modify(|m| {
|
||||
m.remove(&envelope_hash);
|
||||
});
|
||||
self.threads
|
||||
.entry(folder_hash)
|
||||
.entry(mailbox_hash)
|
||||
.or_default()
|
||||
.remove(envelope_hash);
|
||||
for (h, t) in self.threads.iter_mut() {
|
||||
if *h == folder_hash {
|
||||
if *h == mailbox_hash {
|
||||
continue;
|
||||
}
|
||||
t.remove(envelope_hash);
|
||||
|
@ -146,13 +146,13 @@ impl Collection {
|
|||
&mut self,
|
||||
old_hash: EnvelopeHash,
|
||||
new_hash: EnvelopeHash,
|
||||
folder_hash: FolderHash,
|
||||
mailbox_hash: MailboxHash,
|
||||
) {
|
||||
if !self.envelopes.write().unwrap().contains_key(&old_hash) {
|
||||
return;
|
||||
}
|
||||
let mut envelope = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
|
||||
self.mailboxes.entry(folder_hash).and_modify(|m| {
|
||||
self.mailboxes.entry(mailbox_hash).and_modify(|m| {
|
||||
m.remove(&old_hash);
|
||||
m.insert(new_hash);
|
||||
});
|
||||
|
@ -163,7 +163,7 @@ impl Collection {
|
|||
{
|
||||
if self
|
||||
.threads
|
||||
.entry(folder_hash)
|
||||
.entry(mailbox_hash)
|
||||
.or_default()
|
||||
.update_envelope(&self.envelopes, old_hash, new_hash)
|
||||
.is_ok()
|
||||
|
@ -173,11 +173,11 @@ impl Collection {
|
|||
}
|
||||
/* envelope is not in threads, so insert it */
|
||||
self.threads
|
||||
.entry(folder_hash)
|
||||
.entry(mailbox_hash)
|
||||
.or_default()
|
||||
.insert(&mut self.envelopes, new_hash);
|
||||
for (h, t) in self.threads.iter_mut() {
|
||||
if *h == folder_hash {
|
||||
if *h == mailbox_hash {
|
||||
continue;
|
||||
}
|
||||
t.update_envelope(&self.envelopes, old_hash, new_hash)
|
||||
|
@ -187,14 +187,14 @@ impl Collection {
|
|||
}
|
||||
|
||||
/// Merge new mailbox to collection and update threads.
|
||||
/// Returns a list of already existing folders whose threads were updated
|
||||
/// Returns a list of already existing mailboxs whose threads were updated
|
||||
pub fn merge(
|
||||
&mut self,
|
||||
mut new_envelopes: FnvHashMap<EnvelopeHash, Envelope>,
|
||||
folder_hash: FolderHash,
|
||||
sent_folder: Option<FolderHash>,
|
||||
) -> Option<SmallVec<[FolderHash; 8]>> {
|
||||
self.sent_folder = sent_folder;
|
||||
mailbox_hash: MailboxHash,
|
||||
sent_mailbox: Option<MailboxHash>,
|
||||
) -> Option<SmallVec<[MailboxHash; 8]>> {
|
||||
self.sent_mailbox = sent_mailbox;
|
||||
for (h, e) in new_envelopes.iter() {
|
||||
self.message_ids.insert(e.message_id().raw().to_vec(), *h);
|
||||
}
|
||||
|
@ -203,21 +203,21 @@ impl Collection {
|
|||
ref mut threads,
|
||||
ref mut envelopes,
|
||||
ref mut mailboxes,
|
||||
ref sent_folder,
|
||||
ref sent_mailbox,
|
||||
..
|
||||
} = self;
|
||||
|
||||
if !threads.contains_key(&folder_hash) {
|
||||
threads.insert(folder_hash, Threads::new(new_envelopes.len()));
|
||||
mailboxes.insert(folder_hash, new_envelopes.keys().cloned().collect());
|
||||
if !threads.contains_key(&mailbox_hash) {
|
||||
threads.insert(mailbox_hash, Threads::new(new_envelopes.len()));
|
||||
mailboxes.insert(mailbox_hash, new_envelopes.keys().cloned().collect());
|
||||
for (h, e) in new_envelopes {
|
||||
envelopes.write().unwrap().insert(h, e);
|
||||
}
|
||||
} else {
|
||||
mailboxes.entry(folder_hash).and_modify(|m| {
|
||||
mailboxes.entry(mailbox_hash).and_modify(|m| {
|
||||
m.extend(new_envelopes.keys().cloned());
|
||||
});
|
||||
threads.entry(folder_hash).and_modify(|t| {
|
||||
threads.entry(mailbox_hash).and_modify(|t| {
|
||||
let mut ordered_hash_set =
|
||||
new_envelopes.keys().cloned().collect::<Vec<EnvelopeHash>>();
|
||||
ordered_hash_set.sort_by(|a, b| {
|
||||
|
@ -237,14 +237,14 @@ impl Collection {
|
|||
}
|
||||
|
||||
let mut ret = SmallVec::new();
|
||||
let keys = threads.keys().cloned().collect::<Vec<FolderHash>>();
|
||||
let keys = threads.keys().cloned().collect::<Vec<MailboxHash>>();
|
||||
for t_fh in keys {
|
||||
if t_fh == folder_hash {
|
||||
if t_fh == mailbox_hash {
|
||||
continue;
|
||||
}
|
||||
if sent_folder.map(|f| f == folder_hash).unwrap_or(false) {
|
||||
if sent_mailbox.map(|f| f == mailbox_hash).unwrap_or(false) {
|
||||
let envelopes_lck = envelopes.read().unwrap();
|
||||
let mut ordered_hash_set = threads[&folder_hash]
|
||||
let mut ordered_hash_set = threads[&mailbox_hash]
|
||||
.hash_set
|
||||
.iter()
|
||||
.cloned()
|
||||
|
@ -265,7 +265,7 @@ impl Collection {
|
|||
}
|
||||
continue;
|
||||
}
|
||||
if sent_folder.map(|f| f == t_fh).unwrap_or(false) {
|
||||
if sent_mailbox.map(|f| f == t_fh).unwrap_or(false) {
|
||||
let envelopes_lck = envelopes.read().unwrap();
|
||||
let mut ordered_hash_set = threads[&t_fh]
|
||||
.hash_set
|
||||
|
@ -282,12 +282,12 @@ impl Collection {
|
|||
let mut updated = false;
|
||||
for h in ordered_hash_set {
|
||||
updated |= threads
|
||||
.entry(folder_hash)
|
||||
.entry(mailbox_hash)
|
||||
.or_default()
|
||||
.insert_reply(envelopes, h);
|
||||
}
|
||||
if updated {
|
||||
ret.push(folder_hash);
|
||||
ret.push(mailbox_hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -302,19 +302,23 @@ impl Collection {
|
|||
&mut self,
|
||||
old_hash: EnvelopeHash,
|
||||
mut envelope: Envelope,
|
||||
folder_hash: FolderHash,
|
||||
mailbox_hash: MailboxHash,
|
||||
) {
|
||||
let old_env = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
|
||||
envelope.set_thread(old_env.thread());
|
||||
let new_hash = envelope.hash();
|
||||
self.mailboxes.entry(folder_hash).and_modify(|m| {
|
||||
self.mailboxes.entry(mailbox_hash).and_modify(|m| {
|
||||
m.remove(&old_hash);
|
||||
m.insert(new_hash);
|
||||
});
|
||||
self.message_ids
|
||||
.insert(envelope.message_id().raw().to_vec(), new_hash);
|
||||
self.envelopes.write().unwrap().insert(new_hash, envelope);
|
||||
if self.sent_folder.map(|f| f == folder_hash).unwrap_or(false) {
|
||||
if self
|
||||
.sent_mailbox
|
||||
.map(|f| f == mailbox_hash)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
for (_, t) in self.threads.iter_mut() {
|
||||
t.update_envelope(&self.envelopes, old_hash, new_hash)
|
||||
.unwrap_or(());
|
||||
|
@ -323,7 +327,7 @@ impl Collection {
|
|||
{
|
||||
if self
|
||||
.threads
|
||||
.entry(folder_hash)
|
||||
.entry(mailbox_hash)
|
||||
.or_default()
|
||||
.update_envelope(&self.envelopes, old_hash, new_hash)
|
||||
.is_ok()
|
||||
|
@ -333,11 +337,11 @@ impl Collection {
|
|||
}
|
||||
/* envelope is not in threads, so insert it */
|
||||
self.threads
|
||||
.entry(folder_hash)
|
||||
.entry(mailbox_hash)
|
||||
.or_default()
|
||||
.insert(&mut self.envelopes, new_hash);
|
||||
for (h, t) in self.threads.iter_mut() {
|
||||
if *h == folder_hash {
|
||||
if *h == mailbox_hash {
|
||||
continue;
|
||||
}
|
||||
t.update_envelope(&self.envelopes, old_hash, new_hash)
|
||||
|
@ -346,19 +350,23 @@ impl Collection {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, envelope: Envelope, folder_hash: FolderHash) {
|
||||
pub fn insert(&mut self, envelope: Envelope, mailbox_hash: MailboxHash) {
|
||||
let hash = envelope.hash();
|
||||
self.mailboxes.entry(folder_hash).and_modify(|m| {
|
||||
self.mailboxes.entry(mailbox_hash).and_modify(|m| {
|
||||
m.insert(hash);
|
||||
});
|
||||
self.message_ids
|
||||
.insert(envelope.message_id().raw().to_vec(), hash);
|
||||
self.envelopes.write().unwrap().insert(hash, envelope);
|
||||
self.threads
|
||||
.entry(folder_hash)
|
||||
.entry(mailbox_hash)
|
||||
.or_default()
|
||||
.insert(&mut self.envelopes, hash);
|
||||
if self.sent_folder.map(|f| f == folder_hash).unwrap_or(false) {
|
||||
if self
|
||||
.sent_mailbox
|
||||
.map(|f| f == mailbox_hash)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
self.insert_reply(hash);
|
||||
}
|
||||
}
|
||||
|
@ -385,15 +393,15 @@ impl Collection {
|
|||
}
|
||||
}
|
||||
|
||||
impl Index<&FolderHash> for Collection {
|
||||
impl Index<&MailboxHash> for Collection {
|
||||
type Output = FnvHashSet<EnvelopeHash>;
|
||||
fn index(&self, index: &FolderHash) -> &FnvHashSet<EnvelopeHash> {
|
||||
fn index(&self, index: &MailboxHash) -> &FnvHashSet<EnvelopeHash> {
|
||||
&self.mailboxes[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<&FolderHash> for Collection {
|
||||
fn index_mut(&mut self, index: &FolderHash) -> &mut FnvHashSet<EnvelopeHash> {
|
||||
impl IndexMut<&MailboxHash> for Collection {
|
||||
fn index_mut(&mut self, index: &MailboxHash) -> &mut FnvHashSet<EnvelopeHash> {
|
||||
self.mailboxes.get_mut(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,14 +25,14 @@ use std::collections::HashMap;
|
|||
#[derive(Debug, Serialize, Default, Clone)]
|
||||
pub struct AccountSettings {
|
||||
pub name: String,
|
||||
pub root_folder: String,
|
||||
pub root_mailbox: String,
|
||||
pub format: String,
|
||||
pub identity: String,
|
||||
pub read_only: bool,
|
||||
pub display_name: Option<String>,
|
||||
pub subscribed_folders: Vec<String>,
|
||||
pub subscribed_mailboxes: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub folders: HashMap<String, FolderConf>,
|
||||
pub mailboxes: HashMap<String, MailboxConf>,
|
||||
#[serde(default)]
|
||||
pub manual_refresh: bool,
|
||||
#[serde(flatten)]
|
||||
|
@ -49,8 +49,8 @@ impl AccountSettings {
|
|||
pub fn set_name(&mut self, s: String) {
|
||||
self.name = s;
|
||||
}
|
||||
pub fn root_folder(&self) -> &str {
|
||||
&self.root_folder
|
||||
pub fn root_mailbox(&self) -> &str {
|
||||
&self.root_mailbox
|
||||
}
|
||||
pub fn identity(&self) -> &str {
|
||||
&self.identity
|
||||
|
@ -62,8 +62,8 @@ impl AccountSettings {
|
|||
self.display_name.as_ref()
|
||||
}
|
||||
|
||||
pub fn subscribed_folders(&self) -> &Vec<String> {
|
||||
&self.subscribed_folders
|
||||
pub fn subscribed_mailboxes(&self) -> &Vec<String> {
|
||||
&self.subscribed_mailboxes
|
||||
}
|
||||
|
||||
#[cfg(feature = "vcard")]
|
||||
|
@ -74,7 +74,7 @@ impl AccountSettings {
|
|||
|
||||
#[serde(default)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FolderConf {
|
||||
pub struct MailboxConf {
|
||||
pub alias: Option<String>,
|
||||
#[serde(default = "true_val")]
|
||||
pub autoload: bool,
|
||||
|
@ -88,9 +88,9 @@ pub struct FolderConf {
|
|||
pub extra: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Default for FolderConf {
|
||||
impl Default for MailboxConf {
|
||||
fn default() -> Self {
|
||||
FolderConf {
|
||||
MailboxConf {
|
||||
alias: None,
|
||||
autoload: true,
|
||||
subscribe: ToggleFlag::Unset,
|
||||
|
@ -101,7 +101,7 @@ impl Default for FolderConf {
|
|||
}
|
||||
}
|
||||
|
||||
impl FolderConf {
|
||||
impl MailboxConf {
|
||||
pub fn alias(&self) -> Option<&str> {
|
||||
self.alias.as_ref().map(String::as_str)
|
||||
}
|
||||
|
|
|
@ -650,7 +650,7 @@ impl Threads {
|
|||
&mut self,
|
||||
envelopes: &mut Envelopes,
|
||||
env_hash: EnvelopeHash,
|
||||
other_folder: bool,
|
||||
other_mailbox: bool,
|
||||
) -> bool {
|
||||
let envelopes_lck = envelopes.read().unwrap();
|
||||
let reply_to_id: Option<ThreadNodeHash> = envelopes_lck[&env_hash]
|
||||
|
@ -664,7 +664,7 @@ impl Threads {
|
|||
return false;
|
||||
}
|
||||
|
||||
if other_folder
|
||||
if other_mailbox
|
||||
&& reply_to_id.is_none()
|
||||
&& !self.message_ids.contains_key(message_id)
|
||||
&& !envelopes_lck[&env_hash]
|
||||
|
|
|
@ -9,29 +9,29 @@
|
|||
#
|
||||
# Setting up a Maildir account
|
||||
#[accounts.account-name]
|
||||
#root_folder = "/path/to/root/folder"
|
||||
#root_mailbox = "/path/to/root/mailbox"
|
||||
#format = "Maildir"
|
||||
#index_style = "Conversations" # or [plain, threaded, compact]
|
||||
#identity="email@address.tld"
|
||||
#display_name = "Name"
|
||||
#subscribed_folders = ["INBOX", "INBOX/Sent", "INBOX/Drafts", "INBOX/Junk"]
|
||||
#subscribed_mailboxes = ["INBOX", "INBOX/Sent", "INBOX/Drafts", "INBOX/Junk"]
|
||||
|
||||
# Set folder-specific settings
|
||||
# [accounts.account-name.folders]
|
||||
# Set mailbox-specific settings
|
||||
# [accounts.account-name.mailboxes]
|
||||
# "INBOX" = { rename="Inbox" }
|
||||
# "drafts" = { rename="Drafts" }
|
||||
# "foobar-devel" = { ignore = true } # don't show notifications for this folder
|
||||
# "foobar-devel" = { ignore = true } # don't show notifications for this mailbox
|
||||
|
||||
# Setting up an mbox account
|
||||
#[accounts.mbox]
|
||||
#root_folder = "/var/mail/username"
|
||||
#root_mailbox = "/var/mail/username"
|
||||
#format = "mbox"
|
||||
#index_style = "Compact"
|
||||
#identity="username@hostname.local"
|
||||
#
|
||||
# Setting up an IMAP account
|
||||
#[accounts."imap"]
|
||||
#root_folder = "INBOX"
|
||||
#root_mailbox = "INBOX"
|
||||
#format = "imap"
|
||||
#server_hostname="mail.server.tld"
|
||||
#server_password="pha2hiLohs2eeeish2phaii1We3ood4chakaiv0hien2ahie3m"
|
||||
|
@ -42,20 +42,20 @@
|
|||
#index_style = "Conversations"
|
||||
#identity = "username@server.tld"
|
||||
#display_name = "Name Name"
|
||||
## match every folder:
|
||||
#subscribed_folders = ["*" ]
|
||||
## match specific folders:
|
||||
#subscribed_folders = ["INBOX", "INBOX/Sent", "INBOX/Drafts", "INBOX/Junk"]
|
||||
## match every mailbox:
|
||||
#subscribed_mailboxes = ["*" ]
|
||||
## match specific mailboxes:
|
||||
#subscribed_mailboxes = ["INBOX", "INBOX/Sent", "INBOX/Drafts", "INBOX/Junk"]
|
||||
|
||||
# Setting up an account for an already existing notmuch database
|
||||
#[accounts.notmuch]
|
||||
#root_folder = "/path/to/folder" # where .notmuch/ directory is located
|
||||
#root_mailbox = "/path/to/folder" # where .notmuch/ directory is located
|
||||
#format = "notmuch"
|
||||
#index_style = "conversations"
|
||||
#identity="username@server.tld"
|
||||
#display_name = "Name Name"
|
||||
# # notmuch folders are virtual, they are defined by their alias and the notmuch query that corresponds to their content.
|
||||
# [accounts.notmuch.folders]
|
||||
# # notmuch mailboxes are virtual, they are defined by their alias and the notmuch query that corresponds to their content.
|
||||
# [accounts.notmuch.mailboxes]
|
||||
# "INBOX" = { query="tag:inbox", subscribe = true }
|
||||
# "Drafts" = { query="tag:draft", subscribe = true }
|
||||
# "Sent" = { query="from:username@server.tld from:username2@server.tld", subscribe = true }
|
||||
|
@ -88,8 +88,8 @@
|
|||
#[shortcuts.listing]
|
||||
#prev_page = "PageUp"
|
||||
#next_page = "PageDown"
|
||||
#prev_folder = 'K'
|
||||
#next_folder = 'J'
|
||||
#prev_mailbox = 'K'
|
||||
#next_mailbox = 'J'
|
||||
#prev_account = 'l'
|
||||
#next_account = 'h'
|
||||
#new_mail = 'm'
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
use melib::parsec::*;
|
||||
use melib::UnixTimestamp;
|
||||
use melib::{
|
||||
backends::{FolderHash, MailBackend},
|
||||
backends::{MailBackend, MailboxHash},
|
||||
email::EnvelopeHash,
|
||||
thread::{SortField, SortOrder},
|
||||
Result,
|
||||
|
@ -429,7 +429,7 @@ pub fn query_to_imap(q: &Query) -> String {
|
|||
pub fn imap_search(
|
||||
term: &str,
|
||||
(_sort_field, _sort_order): (SortField, SortOrder),
|
||||
folder_hash: FolderHash,
|
||||
mailbox_hash: MailboxHash,
|
||||
backend: &Arc<RwLock<Box<dyn MailBackend>>>,
|
||||
) -> Result<smallvec::SmallVec<[EnvelopeHash; 512]>> {
|
||||
let query = query().parse(term)?.1;
|
||||
|
@ -437,7 +437,7 @@ pub fn imap_search(
|
|||
|
||||
let b = (*backend_lck).as_any();
|
||||
if let Some(imap_backend) = b.downcast_ref::<melib::backends::ImapType>() {
|
||||
imap_backend.search(query_to_imap(&query), folder_hash)
|
||||
imap_backend.search(query_to_imap(&query), mailbox_hash)
|
||||
} else {
|
||||
panic!("Could not downcast ImapType backend. BUG");
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
/*! Entities that handle Mail specific functions.
|
||||
*/
|
||||
use super::*;
|
||||
use melib::backends::Folder;
|
||||
use melib::backends::FolderHash;
|
||||
use melib::backends::Mailbox;
|
||||
use melib::backends::MailboxHash;
|
||||
use melib::thread::ThreadNodeHash;
|
||||
|
||||
pub mod listing;
|
||||
|
|
|
@ -63,7 +63,7 @@ impl std::ops::DerefMut for EmbedStatus {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct Composer {
|
||||
reply_context: Option<(FolderHash, EnvelopeHash)>,
|
||||
reply_context: Option<(MailboxHash, EnvelopeHash)>,
|
||||
account_cursor: usize,
|
||||
|
||||
cursor: Cursor,
|
||||
|
@ -164,7 +164,7 @@ impl Composer {
|
|||
}
|
||||
|
||||
pub fn with_context(
|
||||
coordinates: (usize, FolderHash),
|
||||
coordinates: (usize, MailboxHash),
|
||||
msg: EnvelopeHash,
|
||||
context: &Context,
|
||||
) -> Self {
|
||||
|
@ -1092,7 +1092,7 @@ pub fn send_draft(
|
|||
context: &mut Context,
|
||||
account_cursor: usize,
|
||||
mut draft: Draft,
|
||||
folder_type: SpecialUsageMailbox,
|
||||
mailbox_type: SpecialUsageMailbox,
|
||||
flags: Flag,
|
||||
) -> bool {
|
||||
use std::io::Write;
|
||||
|
@ -1229,7 +1229,7 @@ pub fn send_draft(
|
|||
save_draft(
|
||||
bytes.as_bytes(),
|
||||
context,
|
||||
folder_type,
|
||||
mailbox_type,
|
||||
flags,
|
||||
account_cursor,
|
||||
);
|
||||
|
@ -1239,12 +1239,12 @@ pub fn send_draft(
|
|||
pub fn save_draft(
|
||||
bytes: &[u8],
|
||||
context: &mut Context,
|
||||
folder_type: SpecialUsageMailbox,
|
||||
mailbox_type: SpecialUsageMailbox,
|
||||
flags: Flag,
|
||||
account_cursor: usize,
|
||||
) {
|
||||
if let Err(MeliError { summary, details }) =
|
||||
context.accounts[account_cursor].save_special(bytes, folder_type, flags)
|
||||
context.accounts[account_cursor].save_special(bytes, mailbox_type, flags)
|
||||
{
|
||||
context.replies.push_back(UIEvent::Notification(
|
||||
summary.map(|s| s.into()),
|
||||
|
|
|
@ -125,7 +125,7 @@ struct AccountMenuEntry {
|
|||
name: String,
|
||||
// Index in the config account vector.
|
||||
index: usize,
|
||||
entries: SmallVec<[(usize, FolderHash); 16]>,
|
||||
entries: SmallVec<[(usize, MailboxHash); 16]>,
|
||||
}
|
||||
|
||||
pub trait MailListingTrait: ListingTrait {
|
||||
|
@ -137,10 +137,10 @@ pub trait MailListingTrait: ListingTrait {
|
|||
) {
|
||||
let account = &mut context.accounts[self.coordinates().0];
|
||||
let mut envs_to_set: SmallVec<[EnvelopeHash; 8]> = SmallVec::new();
|
||||
let folder_hash = self.coordinates().1;
|
||||
for (_, h) in account.collection.threads[&folder_hash].thread_group_iter(thread_hash) {
|
||||
let mailbox_hash = self.coordinates().1;
|
||||
for (_, h) in account.collection.threads[&mailbox_hash].thread_group_iter(thread_hash) {
|
||||
envs_to_set.push(
|
||||
account.collection.threads[&folder_hash].thread_nodes()[&h]
|
||||
account.collection.threads[&mailbox_hash].thread_nodes()[&h]
|
||||
.message()
|
||||
.unwrap(),
|
||||
);
|
||||
|
@ -210,8 +210,8 @@ pub trait MailListingTrait: ListingTrait {
|
|||
}
|
||||
|
||||
pub trait ListingTrait: Component {
|
||||
fn coordinates(&self) -> (usize, FolderHash);
|
||||
fn set_coordinates(&mut self, _: (usize, FolderHash));
|
||||
fn coordinates(&self) -> (usize, MailboxHash);
|
||||
fn set_coordinates(&mut self, _: (usize, MailboxHash));
|
||||
fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context);
|
||||
fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context);
|
||||
fn filter(&mut self, _filter_term: &str, _context: &Context) {}
|
||||
|
@ -446,11 +446,11 @@ impl Component for Listing {
|
|||
self.change_account(context);
|
||||
} else {
|
||||
self.accounts[*account_index].entries = context.accounts[*account_index]
|
||||
.list_folders()
|
||||
.list_mailboxes()
|
||||
.into_iter()
|
||||
.filter(|folder_node| {
|
||||
context.accounts[*account_index][&folder_node.hash]
|
||||
.ref_folder
|
||||
.filter(|mailbox_node| {
|
||||
context.accounts[*account_index][&mailbox_node.hash]
|
||||
.ref_mailbox
|
||||
.is_subscribed()
|
||||
})
|
||||
.map(|f| (f.depth, f.hash))
|
||||
|
@ -459,14 +459,14 @@ impl Component for Listing {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
UIEvent::MailboxDelete((account_index, _folder_hash))
|
||||
| UIEvent::MailboxCreate((account_index, _folder_hash)) => {
|
||||
UIEvent::MailboxDelete((account_index, _mailbox_hash))
|
||||
| UIEvent::MailboxCreate((account_index, _mailbox_hash)) => {
|
||||
self.accounts[*account_index].entries = context.accounts[*account_index]
|
||||
.list_folders()
|
||||
.list_mailboxes()
|
||||
.into_iter()
|
||||
.filter(|folder_node| {
|
||||
context.accounts[*account_index][&folder_node.hash]
|
||||
.ref_folder
|
||||
.filter(|mailbox_node| {
|
||||
context.accounts[*account_index][&mailbox_node.hash]
|
||||
.ref_mailbox
|
||||
.is_subscribed()
|
||||
})
|
||||
.map(|f| (f.depth, f.hash))
|
||||
|
@ -495,8 +495,8 @@ impl Component for Listing {
|
|||
let shortcuts = self.get_shortcuts(context);
|
||||
match *event {
|
||||
UIEvent::Input(ref k)
|
||||
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_folder"])
|
||||
|| shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_folder"]) =>
|
||||
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_mailbox"])
|
||||
|| shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_mailbox"]) =>
|
||||
{
|
||||
let amount = if self.cmd_buf.is_empty() {
|
||||
1
|
||||
|
@ -514,28 +514,28 @@ impl Component for Listing {
|
|||
return true;
|
||||
};
|
||||
match k {
|
||||
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_folder"]) => {
|
||||
if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0]
|
||||
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_mailbox"]) => {
|
||||
if let Some((_, mailbox_hash)) = self.accounts[self.cursor_pos.0]
|
||||
.entries
|
||||
.get(self.cursor_pos.1 + amount)
|
||||
{
|
||||
self.cursor_pos.1 += amount;
|
||||
self.component
|
||||
.set_coordinates((self.cursor_pos.0, *folder_hash));
|
||||
.set_coordinates((self.cursor_pos.0, *mailbox_hash));
|
||||
self.set_dirty(true);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_folder"]) => {
|
||||
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_mailbox"]) => {
|
||||
if self.cursor_pos.1 >= amount {
|
||||
if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0]
|
||||
if let Some((_, mailbox_hash)) = self.accounts[self.cursor_pos.0]
|
||||
.entries
|
||||
.get(self.cursor_pos.1 - amount)
|
||||
{
|
||||
self.cursor_pos.1 -= amount;
|
||||
self.component
|
||||
.set_coordinates((self.cursor_pos.0, *folder_hash));
|
||||
.set_coordinates((self.cursor_pos.0, *mailbox_hash));
|
||||
self.set_dirty(true);
|
||||
} else {
|
||||
return true;
|
||||
|
@ -546,16 +546,16 @@ impl Component for Listing {
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0]
|
||||
if let Some((_, mailbox_hash)) = self.accounts[self.cursor_pos.0]
|
||||
.entries
|
||||
.get(self.cursor_pos.1)
|
||||
{
|
||||
/* Account might have no folders yet if it's offline */
|
||||
/* Check if per-folder configuration overrides general configuration */
|
||||
/* Account might have no mailboxes yet if it's offline */
|
||||
/* Check if per-mailbox configuration overrides general configuration */
|
||||
if let Some(index_style) = context
|
||||
.accounts
|
||||
.get(self.cursor_pos.0)
|
||||
.and_then(|account| account[folder_hash].conf.conf_override.index_style)
|
||||
.and_then(|account| account[mailbox_hash].conf.conf_override.index_style)
|
||||
{
|
||||
self.component.set_style(index_style);
|
||||
} else if let Some(index_style) = context
|
||||
|
@ -642,12 +642,12 @@ impl Component for Listing {
|
|||
return true;
|
||||
}
|
||||
Action::ViewMailbox(idx) => {
|
||||
if let Some((_, folder_hash)) =
|
||||
if let Some((_, mailbox_hash)) =
|
||||
self.accounts[self.cursor_pos.0].entries.get(*idx)
|
||||
{
|
||||
self.cursor_pos.1 = *idx;
|
||||
self.component
|
||||
.set_coordinates((self.cursor_pos.0, *folder_hash));
|
||||
.set_coordinates((self.cursor_pos.0, *mailbox_hash));
|
||||
self.set_dirty(true);
|
||||
} else {
|
||||
return true;
|
||||
|
@ -791,8 +791,8 @@ impl Component for Listing {
|
|||
if shortcut!(key == shortcuts[Listing::DESCRIPTION]["refresh"]) =>
|
||||
{
|
||||
let account = &mut context.accounts[self.cursor_pos.0];
|
||||
if let Some(&folder_hash) = account.folders_order.get(self.cursor_pos.1) {
|
||||
if let Err(err) = account.refresh(folder_hash) {
|
||||
if let Some(&mailbox_hash) = account.mailboxes_order.get(self.cursor_pos.1) {
|
||||
if let Err(err) = account.refresh(mailbox_hash) {
|
||||
context.replies.push_back(UIEvent::Notification(
|
||||
Some("Could not refresh.".to_string()),
|
||||
err.to_string(),
|
||||
|
@ -862,36 +862,36 @@ impl Component for Listing {
|
|||
}
|
||||
|
||||
fn get_status(&self, context: &Context) -> String {
|
||||
let folder_hash = if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0]
|
||||
let mailbox_hash = if let Some((_, mailbox_hash)) = self.accounts[self.cursor_pos.0]
|
||||
.entries
|
||||
.get(self.cursor_pos.1)
|
||||
{
|
||||
*folder_hash
|
||||
*mailbox_hash
|
||||
} else {
|
||||
return String::new();
|
||||
};
|
||||
|
||||
let account = &context.accounts[self.cursor_pos.0];
|
||||
use crate::conf::accounts::MailboxStatus;
|
||||
match account[&folder_hash].status {
|
||||
match account[&mailbox_hash].status {
|
||||
MailboxStatus::Available | MailboxStatus::Parsing(_, _) => format!(
|
||||
"Mailbox: {}, Messages: {}, New: {}",
|
||||
account[&folder_hash].ref_folder.name(),
|
||||
account.collection[&folder_hash].len(),
|
||||
account[&folder_hash]
|
||||
.ref_folder
|
||||
account[&mailbox_hash].ref_mailbox.name(),
|
||||
account.collection[&mailbox_hash].len(),
|
||||
account[&mailbox_hash]
|
||||
.ref_mailbox
|
||||
.count()
|
||||
.ok()
|
||||
.map(|(v, _)| v)
|
||||
.unwrap_or(0),
|
||||
),
|
||||
MailboxStatus::Failed(_) | MailboxStatus::None => account[&folder_hash].status(),
|
||||
MailboxStatus::Failed(_) | MailboxStatus::None => account[&mailbox_hash].status(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(IndexStyle, (usize, FolderHash))> for ListingComponent {
|
||||
fn from((index_style, coordinates): (IndexStyle, (usize, FolderHash))) -> Self {
|
||||
impl From<(IndexStyle, (usize, MailboxHash))> for ListingComponent {
|
||||
fn from((index_style, coordinates): (IndexStyle, (usize, MailboxHash))) -> Self {
|
||||
match index_style {
|
||||
IndexStyle::Plain => Plain(PlainListing::new(coordinates)),
|
||||
IndexStyle::Threaded => Threaded(ThreadListing::new(coordinates)),
|
||||
|
@ -909,10 +909,10 @@ impl Listing {
|
|||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, a)| {
|
||||
let entries: SmallVec<[(usize, FolderHash); 16]> = a
|
||||
.list_folders()
|
||||
let entries: SmallVec<[(usize, MailboxHash); 16]> = a
|
||||
.list_mailboxes()
|
||||
.into_iter()
|
||||
.filter(|folder_node| a[&folder_node.hash].ref_folder.is_subscribed())
|
||||
.filter(|mailbox_node| a[&mailbox_node.hash].ref_mailbox.is_subscribed())
|
||||
.map(|f| (f.depth, f.hash))
|
||||
.collect::<_>();
|
||||
|
||||
|
@ -981,10 +981,10 @@ impl Listing {
|
|||
debug!("BUG: invalid area in print_account");
|
||||
}
|
||||
// Each entry and its index in the account
|
||||
let folders: FnvHashMap<FolderHash, Folder> = context.accounts[a.index]
|
||||
.folder_entries
|
||||
let mailboxes: FnvHashMap<MailboxHash, Mailbox> = context.accounts[a.index]
|
||||
.mailbox_entries
|
||||
.iter()
|
||||
.map(|(&hash, entry)| (hash, entry.ref_folder.clone()))
|
||||
.map(|(&hash, entry)| (hash, entry.ref_mailbox.clone()))
|
||||
.collect();
|
||||
|
||||
let upper_left = upper_left!(area);
|
||||
|
@ -992,21 +992,21 @@ impl Listing {
|
|||
|
||||
let must_highlight_account: bool = self.cursor_pos.0 == a.index;
|
||||
|
||||
let mut lines: Vec<(usize, usize, FolderHash, Option<usize>)> = Vec::new();
|
||||
let mut lines: Vec<(usize, usize, MailboxHash, Option<usize>)> = Vec::new();
|
||||
|
||||
for (i, &(depth, folder_hash)) in a.entries.iter().enumerate() {
|
||||
if folders[&folder_hash].is_subscribed() {
|
||||
match context.accounts[a.index].status(folder_hash) {
|
||||
for (i, &(depth, mailbox_hash)) in a.entries.iter().enumerate() {
|
||||
if mailboxes[&mailbox_hash].is_subscribed() {
|
||||
match context.accounts[a.index].status(mailbox_hash) {
|
||||
Ok(_) => {
|
||||
lines.push((
|
||||
depth,
|
||||
i,
|
||||
folder_hash,
|
||||
folders[&folder_hash].count().ok().map(|(v, _)| v),
|
||||
mailbox_hash,
|
||||
mailboxes[&mailbox_hash].count().ok().map(|(v, _)| v),
|
||||
));
|
||||
}
|
||||
Err(_) => {
|
||||
lines.push((depth, i, folder_hash, None));
|
||||
lines.push((depth, i, mailbox_hash, None));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1075,8 +1075,8 @@ impl Listing {
|
|||
)
|
||||
};
|
||||
|
||||
let (depth, inc, folder_idx, count) = lines[idx];
|
||||
/* Calculate how many columns the folder index tags should occupy with right alignment,
|
||||
let (depth, inc, mailbox_idx, count) = lines[idx];
|
||||
/* Calculate how many columns the mailbox index tags should occupy with right alignment,
|
||||
* eg.
|
||||
* 1
|
||||
* 2
|
||||
|
@ -1084,7 +1084,7 @@ impl Listing {
|
|||
* 9
|
||||
* 10
|
||||
*/
|
||||
let total_folder_no_digits = {
|
||||
let total_mailbox_no_digits = {
|
||||
let mut len = lines_len;
|
||||
let mut ctr = 1;
|
||||
while len > 9 {
|
||||
|
@ -1094,7 +1094,7 @@ impl Listing {
|
|||
ctr
|
||||
};
|
||||
let (x, _) = write_string_to_grid(
|
||||
&format!("{:>width$}", inc, width = total_folder_no_digits),
|
||||
&format!("{:>width$}", inc, width = total_mailbox_no_digits),
|
||||
grid,
|
||||
index_att.fg,
|
||||
index_att.bg,
|
||||
|
@ -1112,7 +1112,7 @@ impl Listing {
|
|||
None,
|
||||
);
|
||||
let (x, _) = write_string_to_grid(
|
||||
folders[&folder_idx].name(),
|
||||
mailboxes[&mailbox_idx].name(),
|
||||
grid,
|
||||
att.fg,
|
||||
att.bg,
|
||||
|
@ -1145,7 +1145,7 @@ impl Listing {
|
|||
},
|
||||
(
|
||||
(
|
||||
/* Hide part of folder name if need be to fit the message count */
|
||||
/* Hide part of mailbox name if need be to fit the message count */
|
||||
std::cmp::min(x, get_x(bottom_right).saturating_sub(count_string.len())),
|
||||
y,
|
||||
),
|
||||
|
@ -1167,27 +1167,27 @@ impl Listing {
|
|||
|
||||
fn change_account(&mut self, context: &mut Context) {
|
||||
self.accounts[self.cursor_pos.0].entries = context.accounts[self.cursor_pos.0]
|
||||
.list_folders()
|
||||
.list_mailboxes()
|
||||
.into_iter()
|
||||
.filter(|folder_node| {
|
||||
context.accounts[self.cursor_pos.0][&folder_node.hash]
|
||||
.ref_folder
|
||||
.filter(|mailbox_node| {
|
||||
context.accounts[self.cursor_pos.0][&mailbox_node.hash]
|
||||
.ref_mailbox
|
||||
.is_subscribed()
|
||||
})
|
||||
.map(|f| (f.depth, f.hash))
|
||||
.collect::<_>();
|
||||
/* Account might have no folders yet if it's offline */
|
||||
if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0]
|
||||
/* Account might have no mailboxes yet if it's offline */
|
||||
if let Some((_, mailbox_hash)) = self.accounts[self.cursor_pos.0]
|
||||
.entries
|
||||
.get(self.cursor_pos.1)
|
||||
{
|
||||
self.component
|
||||
.set_coordinates((self.cursor_pos.0, *folder_hash));
|
||||
/* Check if per-folder configuration overrides general configuration */
|
||||
.set_coordinates((self.cursor_pos.0, *mailbox_hash));
|
||||
/* Check if per-mailbox configuration overrides general configuration */
|
||||
if let Some(index_style) = context
|
||||
.accounts
|
||||
.get(self.cursor_pos.0)
|
||||
.and_then(|account| account[folder_hash].conf.conf_override.index_style)
|
||||
.and_then(|account| account[mailbox_hash].conf.conf_override.index_style)
|
||||
{
|
||||
self.component.set_style(index_style);
|
||||
} else if let Some(index_style) = context
|
||||
|
|
|
@ -48,9 +48,9 @@ macro_rules! address_list {
|
|||
/// `ThreadView`.
|
||||
#[derive(Debug)]
|
||||
pub struct CompactListing {
|
||||
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
|
||||
cursor_pos: (usize, FolderHash, usize),
|
||||
new_cursor_pos: (usize, FolderHash, usize),
|
||||
/// (x, y, z): x is accounts, y is mailboxes, z is index inside a mailbox.
|
||||
cursor_pos: (usize, MailboxHash, usize),
|
||||
new_cursor_pos: (usize, MailboxHash, usize),
|
||||
length: usize,
|
||||
sort: (SortField, SortOrder),
|
||||
subsort: (SortField, SortOrder),
|
||||
|
@ -100,7 +100,7 @@ impl MailListingTrait for CompactListing {
|
|||
SmallVec::from_iter(iter.into_iter())
|
||||
}
|
||||
|
||||
/// Fill the `self.data_columns` `CellBuffers` with the contents of the account folder the user has
|
||||
/// Fill the `self.data_columns` `CellBuffers` with the contents of the account mailbox the user has
|
||||
/// chosen.
|
||||
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
|
||||
self.dirty = true;
|
||||
|
@ -184,11 +184,11 @@ impl MailListingTrait for CompactListing {
|
|||
}
|
||||
|
||||
impl ListingTrait for CompactListing {
|
||||
fn coordinates(&self) -> (usize, FolderHash) {
|
||||
fn coordinates(&self) -> (usize, MailboxHash) {
|
||||
(self.new_cursor_pos.0, self.new_cursor_pos.1)
|
||||
}
|
||||
|
||||
fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) {
|
||||
fn set_coordinates(&mut self, coordinates: (usize, MailboxHash)) {
|
||||
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
|
||||
self.unfocused = false;
|
||||
self.view = ThreadView::default();
|
||||
|
@ -618,7 +618,7 @@ impl fmt::Display for CompactListing {
|
|||
|
||||
impl CompactListing {
|
||||
const DESCRIPTION: &'static str = "compact listing";
|
||||
pub fn new(coordinates: (usize, FolderHash)) -> Self {
|
||||
pub fn new(coordinates: (usize, MailboxHash)) -> Self {
|
||||
CompactListing {
|
||||
cursor_pos: (0, 1, 0),
|
||||
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
||||
|
@ -650,14 +650,14 @@ impl CompactListing {
|
|||
hash: ThreadHash,
|
||||
) -> EntryStrings {
|
||||
let thread = threads.thread_ref(hash);
|
||||
let folder = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
|
||||
let mailbox = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
|
||||
let mut tags = String::new();
|
||||
let mut colors: SmallVec<[_; 8]> = SmallVec::new();
|
||||
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
||||
if let Some(t) = backend_lck.tags() {
|
||||
let tags_lck = t.read().unwrap();
|
||||
for t in e.labels().iter() {
|
||||
if folder
|
||||
if mailbox
|
||||
.conf_override
|
||||
.tags
|
||||
.as_ref()
|
||||
|
@ -669,7 +669,7 @@ impl CompactListing {
|
|||
tags.push(' ');
|
||||
tags.push_str(tags_lck.get(t).as_ref().unwrap());
|
||||
tags.push(' ');
|
||||
if let Some(&c) = folder
|
||||
if let Some(&c) = mailbox
|
||||
.conf_override
|
||||
.tags
|
||||
.as_ref()
|
||||
|
|
|
@ -27,9 +27,9 @@ use std::iter::FromIterator;
|
|||
/// `ThreadView`.
|
||||
#[derive(Debug)]
|
||||
pub struct ConversationsListing {
|
||||
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
|
||||
cursor_pos: (usize, FolderHash, usize),
|
||||
new_cursor_pos: (usize, FolderHash, usize),
|
||||
/// (x, y, z): x is accounts, y is mailboxes, z is index inside a mailbox.
|
||||
cursor_pos: (usize, MailboxHash, usize),
|
||||
new_cursor_pos: (usize, MailboxHash, usize),
|
||||
length: usize,
|
||||
sort: (SortField, SortOrder),
|
||||
subsort: (SortField, SortOrder),
|
||||
|
@ -79,11 +79,11 @@ impl MailListingTrait for ConversationsListing {
|
|||
SmallVec::from_iter(iter.into_iter())
|
||||
}
|
||||
|
||||
/// Fill the `self.data_columns` `CellBuffers` with the contents of the account folder the user has
|
||||
/// Fill the `self.data_columns` `CellBuffers` with the contents of the account mailbox the user has
|
||||
/// chosen.
|
||||
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
|
||||
self.dirty = true;
|
||||
let old_folder_hash = self.cursor_pos.1;
|
||||
let old_mailbox_hash = self.cursor_pos.1;
|
||||
let old_cursor_pos = self.cursor_pos;
|
||||
if !(self.cursor_pos.0 == self.new_cursor_pos.0
|
||||
&& self.cursor_pos.1 == self.new_cursor_pos.1)
|
||||
|
@ -159,7 +159,8 @@ impl MailListingTrait for ConversationsListing {
|
|||
Box::new(roots.into_iter()) as Box<dyn Iterator<Item = ThreadHash>>,
|
||||
);
|
||||
|
||||
if !force && old_cursor_pos == self.new_cursor_pos && old_folder_hash == self.cursor_pos.1 {
|
||||
if !force && old_cursor_pos == self.new_cursor_pos && old_mailbox_hash == self.cursor_pos.1
|
||||
{
|
||||
self.view.update(context);
|
||||
} else if self.unfocused {
|
||||
let thread_group = self.get_thread_under_cursor(self.cursor_pos.2);
|
||||
|
@ -170,11 +171,11 @@ impl MailListingTrait for ConversationsListing {
|
|||
}
|
||||
|
||||
impl ListingTrait for ConversationsListing {
|
||||
fn coordinates(&self) -> (usize, FolderHash) {
|
||||
fn coordinates(&self) -> (usize, MailboxHash) {
|
||||
(self.new_cursor_pos.0, self.new_cursor_pos.1)
|
||||
}
|
||||
|
||||
fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) {
|
||||
fn set_coordinates(&mut self, coordinates: (usize, MailboxHash)) {
|
||||
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
|
||||
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
|
||||
self.unfocused = false;
|
||||
|
@ -559,7 +560,7 @@ impl fmt::Display for ConversationsListing {
|
|||
|
||||
impl ConversationsListing {
|
||||
const DESCRIPTION: &'static str = "compact listing";
|
||||
pub fn new(coordinates: (usize, FolderHash)) -> Self {
|
||||
pub fn new(coordinates: (usize, MailboxHash)) -> Self {
|
||||
ConversationsListing {
|
||||
cursor_pos: (0, 1, 0),
|
||||
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
||||
|
@ -592,14 +593,14 @@ impl ConversationsListing {
|
|||
hash: ThreadHash,
|
||||
) -> EntryStrings {
|
||||
let thread = threads.thread_ref(hash);
|
||||
let folder = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
|
||||
let mailbox = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
|
||||
let mut tags = String::new();
|
||||
let mut colors = SmallVec::new();
|
||||
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
||||
if let Some(t) = backend_lck.tags() {
|
||||
let tags_lck = t.read().unwrap();
|
||||
for t in e.labels().iter() {
|
||||
if folder
|
||||
if mailbox
|
||||
.conf_override
|
||||
.tags
|
||||
.as_ref()
|
||||
|
@ -611,7 +612,7 @@ impl ConversationsListing {
|
|||
tags.push(' ');
|
||||
tags.push_str(tags_lck.get(t).as_ref().unwrap());
|
||||
tags.push(' ');
|
||||
if let Some(&c) = folder
|
||||
if let Some(&c) = mailbox
|
||||
.conf_override
|
||||
.tags
|
||||
.as_ref()
|
||||
|
|
|
@ -24,7 +24,7 @@ use crate::components::utilities::PageMovement;
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct OfflineListing {
|
||||
cursor_pos: (usize, FolderHash),
|
||||
cursor_pos: (usize, MailboxHash),
|
||||
_row_updates: SmallVec<[ThreadHash; 8]>,
|
||||
|
||||
id: ComponentId,
|
||||
|
@ -44,11 +44,11 @@ impl MailListingTrait for OfflineListing {
|
|||
}
|
||||
|
||||
impl ListingTrait for OfflineListing {
|
||||
fn coordinates(&self) -> (usize, FolderHash) {
|
||||
fn coordinates(&self) -> (usize, MailboxHash) {
|
||||
self.cursor_pos
|
||||
}
|
||||
|
||||
fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) {
|
||||
fn set_coordinates(&mut self, coordinates: (usize, MailboxHash)) {
|
||||
self.cursor_pos = coordinates;
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ impl fmt::Display for OfflineListing {
|
|||
}
|
||||
|
||||
impl OfflineListing {
|
||||
pub fn new(cursor_pos: (usize, FolderHash)) -> Self {
|
||||
pub fn new(cursor_pos: (usize, MailboxHash)) -> Self {
|
||||
OfflineListing {
|
||||
cursor_pos,
|
||||
_row_updates: SmallVec::new(),
|
||||
|
|
|
@ -47,9 +47,9 @@ macro_rules! address_list {
|
|||
/// `MailView`.
|
||||
#[derive(Debug)]
|
||||
pub struct PlainListing {
|
||||
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
|
||||
cursor_pos: (usize, FolderHash, usize),
|
||||
new_cursor_pos: (usize, FolderHash, usize),
|
||||
/// (x, y, z): x is accounts, y is mailboxes, z is index inside a mailbox.
|
||||
cursor_pos: (usize, MailboxHash, usize),
|
||||
new_cursor_pos: (usize, MailboxHash, usize),
|
||||
length: usize,
|
||||
sort: (SortField, SortOrder),
|
||||
subsort: (SortField, SortOrder),
|
||||
|
@ -101,7 +101,7 @@ impl MailListingTrait for PlainListing {
|
|||
*/
|
||||
}
|
||||
|
||||
/// Fill the `self.data_columns` `CellBuffers` with the contents of the account folder the user has
|
||||
/// Fill the `self.data_columns` `CellBuffers` with the contents of the account mailbox the user has
|
||||
/// chosen.
|
||||
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
|
||||
self.dirty = true;
|
||||
|
@ -188,11 +188,11 @@ impl MailListingTrait for PlainListing {
|
|||
}
|
||||
|
||||
impl ListingTrait for PlainListing {
|
||||
fn coordinates(&self) -> (usize, FolderHash) {
|
||||
fn coordinates(&self) -> (usize, MailboxHash) {
|
||||
(self.new_cursor_pos.0, self.new_cursor_pos.1)
|
||||
}
|
||||
|
||||
fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) {
|
||||
fn set_coordinates(&mut self, coordinates: (usize, MailboxHash)) {
|
||||
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
|
||||
self.unfocused = false;
|
||||
self.view = MailView::default();
|
||||
|
@ -584,7 +584,7 @@ impl fmt::Display for PlainListing {
|
|||
|
||||
impl PlainListing {
|
||||
const DESCRIPTION: &'static str = "plain listing";
|
||||
pub fn new(coordinates: (usize, FolderHash)) -> Self {
|
||||
pub fn new(coordinates: (usize, MailboxHash)) -> Self {
|
||||
PlainListing {
|
||||
cursor_pos: (0, 1, 0),
|
||||
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
||||
|
@ -613,14 +613,14 @@ impl PlainListing {
|
|||
}
|
||||
}
|
||||
fn make_entry_string(&self, e: EnvelopeRef, context: &Context) -> EntryStrings {
|
||||
let folder = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
|
||||
let mailbox = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
|
||||
let mut tags = String::new();
|
||||
let mut colors = SmallVec::new();
|
||||
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
||||
if let Some(t) = backend_lck.tags() {
|
||||
let tags_lck = t.read().unwrap();
|
||||
for t in e.labels().iter() {
|
||||
if folder
|
||||
if mailbox
|
||||
.conf_override
|
||||
.tags
|
||||
.as_ref()
|
||||
|
@ -632,7 +632,7 @@ impl PlainListing {
|
|||
tags.push(' ');
|
||||
tags.push_str(tags_lck.get(t).as_ref().unwrap());
|
||||
tags.push(' ');
|
||||
if let Some(&c) = folder
|
||||
if let Some(&c) = mailbox
|
||||
.conf_override
|
||||
.tags
|
||||
.as_ref()
|
||||
|
|
|
@ -28,9 +28,9 @@ const MAX_COLS: usize = 500;
|
|||
/// `MailView`.
|
||||
#[derive(Debug)]
|
||||
pub struct ThreadListing {
|
||||
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
|
||||
cursor_pos: (usize, FolderHash, usize),
|
||||
new_cursor_pos: (usize, FolderHash, usize),
|
||||
/// (x, y, z): x is accounts, y is mailboxes, z is index inside a mailbox.
|
||||
cursor_pos: (usize, MailboxHash, usize),
|
||||
new_cursor_pos: (usize, MailboxHash, usize),
|
||||
length: usize,
|
||||
sort: (SortField, SortOrder),
|
||||
subsort: (SortField, SortOrder),
|
||||
|
@ -59,7 +59,7 @@ impl MailListingTrait for ThreadListing {
|
|||
SmallVec::new()
|
||||
}
|
||||
|
||||
/// Fill the `self.content` `CellBuffer` with the contents of the account folder the user has
|
||||
/// Fill the `self.content` `CellBuffer` with the contents of the account mailbox the user has
|
||||
/// chosen.
|
||||
fn refresh_mailbox(&mut self, context: &mut Context, _force: bool) {
|
||||
self.dirty = true;
|
||||
|
@ -127,7 +127,7 @@ impl MailListingTrait for ThreadListing {
|
|||
ret
|
||||
};
|
||||
if threads.len() == 0 {
|
||||
let message = format!("Folder `{}` is empty.", account[&self.cursor_pos.1].name());
|
||||
let message = format!("Mailbox `{}` is empty.", account[&self.cursor_pos.1].name());
|
||||
self.content = CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
||||
write_string_to_grid(
|
||||
&message,
|
||||
|
@ -226,10 +226,10 @@ impl MailListingTrait for ThreadListing {
|
|||
}
|
||||
|
||||
impl ListingTrait for ThreadListing {
|
||||
fn coordinates(&self) -> (usize, FolderHash) {
|
||||
fn coordinates(&self) -> (usize, MailboxHash) {
|
||||
(self.new_cursor_pos.0, self.new_cursor_pos.1)
|
||||
}
|
||||
fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) {
|
||||
fn set_coordinates(&mut self, coordinates: (usize, MailboxHash)) {
|
||||
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
|
||||
self.unfocused = false;
|
||||
self.view = None;
|
||||
|
@ -396,7 +396,7 @@ impl fmt::Display for ThreadListing {
|
|||
}
|
||||
|
||||
impl ThreadListing {
|
||||
pub fn new(coordinates: (usize, FolderHash)) -> Self {
|
||||
pub fn new(coordinates: (usize, MailboxHash)) -> Self {
|
||||
ThreadListing {
|
||||
cursor_pos: (0, 1, 0),
|
||||
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
||||
|
|
|
@ -308,9 +308,9 @@ impl StatusPanel {
|
|||
}
|
||||
}
|
||||
let count = a
|
||||
.folder_entries
|
||||
.mailbox_entries
|
||||
.values()
|
||||
.map(|entry| &entry.ref_folder)
|
||||
.map(|entry| &entry.ref_mailbox)
|
||||
.fold((0, 0), |acc, f| {
|
||||
let count = f.count().unwrap_or((0, 0));
|
||||
(acc.0 + count.0, acc.1 + count.1)
|
||||
|
@ -361,9 +361,9 @@ impl StatusPanel {
|
|||
None,
|
||||
);
|
||||
for (i, f) in a
|
||||
.folder_entries
|
||||
.mailbox_entries
|
||||
.values()
|
||||
.map(|entry| &entry.ref_folder)
|
||||
.map(|entry| &entry.ref_mailbox)
|
||||
.filter(|f| f.special_usage() != SpecialUsageMailbox::Normal)
|
||||
.enumerate()
|
||||
{
|
||||
|
@ -479,9 +479,9 @@ impl Component for AccountStatus {
|
|||
None,
|
||||
);
|
||||
for f in a
|
||||
.folder_entries
|
||||
.mailbox_entries
|
||||
.values()
|
||||
.map(|entry| &entry.ref_folder)
|
||||
.map(|entry| &entry.ref_mailbox)
|
||||
.filter(|f| f.special_usage() != SpecialUsageMailbox::Normal)
|
||||
{
|
||||
line += 1;
|
||||
|
@ -497,7 +497,7 @@ impl Component for AccountStatus {
|
|||
}
|
||||
line += 2;
|
||||
write_string_to_grid(
|
||||
"Subscribed folders:",
|
||||
"Subscribed mailboxes:",
|
||||
&mut self.content,
|
||||
self.theme_default.fg,
|
||||
self.theme_default.bg,
|
||||
|
@ -506,8 +506,8 @@ impl Component for AccountStatus {
|
|||
None,
|
||||
);
|
||||
line += 2;
|
||||
for folder_node in a.list_folders() {
|
||||
let f: &Folder = &a[&folder_node.hash].ref_folder;
|
||||
for mailbox_node in a.list_mailboxes() {
|
||||
let f: &Mailbox = &a[&mailbox_node.hash].ref_mailbox;
|
||||
if f.is_subscribed() {
|
||||
write_string_to_grid(
|
||||
f.path(),
|
||||
|
|
|
@ -86,7 +86,7 @@ impl ViewMode {
|
|||
/// menus
|
||||
#[derive(Debug, Default)]
|
||||
pub struct MailView {
|
||||
coordinates: (usize, FolderHash, EnvelopeHash),
|
||||
coordinates: (usize, MailboxHash, EnvelopeHash),
|
||||
pager: Pager,
|
||||
subview: Option<Box<dyn Component>>,
|
||||
dirty: bool,
|
||||
|
@ -124,7 +124,7 @@ impl fmt::Display for MailView {
|
|||
impl MailView {
|
||||
const DESCRIPTION: &'static str = "view mail";
|
||||
pub fn new(
|
||||
coordinates: (usize, FolderHash, EnvelopeHash),
|
||||
coordinates: (usize, MailboxHash, EnvelopeHash),
|
||||
pager: Option<Pager>,
|
||||
subview: Option<Box<dyn Component>>,
|
||||
context: &Context,
|
||||
|
@ -342,7 +342,7 @@ impl MailView {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, new_coordinates: (usize, FolderHash, EnvelopeHash)) {
|
||||
pub fn update(&mut self, new_coordinates: (usize, MailboxHash, EnvelopeHash)) {
|
||||
self.coordinates = new_coordinates;
|
||||
self.mode = ViewMode::Normal;
|
||||
self.initialised = false;
|
||||
|
|
|
@ -51,7 +51,7 @@ pub struct ThreadView {
|
|||
expanded_pos: usize,
|
||||
new_expanded_pos: usize,
|
||||
reversed: bool,
|
||||
coordinates: (usize, FolderHash, usize),
|
||||
coordinates: (usize, MailboxHash, usize),
|
||||
thread_group: ThreadHash,
|
||||
mailview: MailView,
|
||||
show_mailview: bool,
|
||||
|
@ -69,13 +69,13 @@ pub struct ThreadView {
|
|||
impl ThreadView {
|
||||
const DESCRIPTION: &'static str = "thread view";
|
||||
/*
|
||||
* coordinates: (account index, folder_hash, root set thread_node index)
|
||||
* coordinates: (account index, mailbox_hash, root set thread_node index)
|
||||
* expanded_hash: optional position of expanded entry when we render the threadview. Default
|
||||
* expanded message is the last one.
|
||||
* context: current context
|
||||
*/
|
||||
pub fn new(
|
||||
coordinates: (usize, FolderHash, usize),
|
||||
coordinates: (usize, MailboxHash, usize),
|
||||
thread_group: ThreadHash,
|
||||
expanded_hash: Option<ThreadNodeHash>,
|
||||
context: &Context,
|
||||
|
|
64
src/conf.rs
64
src/conf.rs
|
@ -51,7 +51,7 @@ use self::notifications::NotificationsSettings;
|
|||
use self::terminal::TerminalSettings;
|
||||
use crate::pager::PagerSettings;
|
||||
use crate::plugins::Plugin;
|
||||
use melib::conf::{AccountSettings, FolderConf, ToggleFlag};
|
||||
use melib::conf::{AccountSettings, MailboxConf, ToggleFlag};
|
||||
use melib::error::*;
|
||||
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
@ -85,27 +85,27 @@ pub struct MailUIConf {
|
|||
|
||||
#[serde(default)]
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
pub struct FileFolderConf {
|
||||
pub struct FileMailboxConf {
|
||||
#[serde(flatten)]
|
||||
pub conf_override: MailUIConf,
|
||||
#[serde(flatten)]
|
||||
pub folder_conf: FolderConf,
|
||||
pub mailbox_conf: MailboxConf,
|
||||
}
|
||||
|
||||
impl FileFolderConf {
|
||||
impl FileMailboxConf {
|
||||
pub fn conf_override(&self) -> &MailUIConf {
|
||||
&self.conf_override
|
||||
}
|
||||
|
||||
pub fn folder_conf(&self) -> &FolderConf {
|
||||
&self.folder_conf
|
||||
pub fn mailbox_conf(&self) -> &MailboxConf {
|
||||
&self.mailbox_conf
|
||||
}
|
||||
}
|
||||
|
||||
use crate::conf::deserializers::extra_settings;
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct FileAccount {
|
||||
root_folder: String,
|
||||
root_mailbox: String,
|
||||
format: String,
|
||||
identity: String,
|
||||
#[serde(default = "none")]
|
||||
|
@ -115,9 +115,9 @@ pub struct FileAccount {
|
|||
#[serde(default = "false_val")]
|
||||
read_only: bool,
|
||||
#[serde(default)]
|
||||
subscribed_folders: Vec<String>,
|
||||
subscribed_mailboxes: Vec<String>,
|
||||
#[serde(default)]
|
||||
folders: HashMap<String, FileFolderConf>,
|
||||
mailboxes: HashMap<String, FileMailboxConf>,
|
||||
#[serde(default)]
|
||||
cache_type: CacheType,
|
||||
#[serde(default)]
|
||||
|
@ -132,44 +132,44 @@ pub struct FileAccount {
|
|||
impl From<FileAccount> for AccountConf {
|
||||
fn from(x: FileAccount) -> Self {
|
||||
let format = x.format.to_lowercase();
|
||||
let root_folder = x.root_folder.clone();
|
||||
let root_mailbox = x.root_mailbox.clone();
|
||||
let identity = x.identity.clone();
|
||||
let display_name = x.display_name.clone();
|
||||
let folders = x
|
||||
.folders
|
||||
let mailboxes = x
|
||||
.mailboxes
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.folder_conf.clone()))
|
||||
.map(|(k, v)| (k.clone(), v.mailbox_conf.clone()))
|
||||
.collect();
|
||||
|
||||
let acc = AccountSettings {
|
||||
name: String::new(),
|
||||
root_folder,
|
||||
root_mailbox,
|
||||
format,
|
||||
identity,
|
||||
read_only: x.read_only,
|
||||
display_name,
|
||||
subscribed_folders: x.subscribed_folders.clone(),
|
||||
folders,
|
||||
subscribed_mailboxes: x.subscribed_mailboxes.clone(),
|
||||
mailboxes,
|
||||
manual_refresh: x.manual_refresh,
|
||||
extra: x.extra.clone(),
|
||||
};
|
||||
|
||||
let folder_confs = x.folders.clone();
|
||||
let mailbox_confs = x.mailboxes.clone();
|
||||
AccountConf {
|
||||
account: acc,
|
||||
conf: x,
|
||||
folder_confs,
|
||||
mailbox_confs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FileAccount {
|
||||
pub fn folders(&self) -> &HashMap<String, FileFolderConf> {
|
||||
&self.folders
|
||||
pub fn mailboxes(&self) -> &HashMap<String, FileMailboxConf> {
|
||||
&self.mailboxes
|
||||
}
|
||||
|
||||
pub fn folder(&self) -> &str {
|
||||
&self.root_folder
|
||||
pub fn mailbox(&self) -> &str {
|
||||
&self.root_mailbox
|
||||
}
|
||||
|
||||
pub fn index_style(&self) -> IndexStyle {
|
||||
|
@ -207,7 +207,7 @@ pub struct FileSettings {
|
|||
pub struct AccountConf {
|
||||
pub(crate) account: AccountSettings,
|
||||
pub(crate) conf: FileAccount,
|
||||
pub(crate) folder_confs: HashMap<String, FileFolderConf>,
|
||||
pub(crate) mailbox_confs: HashMap<String, FileMailboxConf>,
|
||||
}
|
||||
|
||||
impl AccountConf {
|
||||
|
@ -342,13 +342,13 @@ impl FileSettings {
|
|||
s.terminal.themes.validate()?;
|
||||
for (name, acc) in &s.accounts {
|
||||
let FileAccount {
|
||||
root_folder,
|
||||
root_mailbox,
|
||||
format,
|
||||
identity,
|
||||
read_only,
|
||||
display_name,
|
||||
subscribed_folders,
|
||||
folders,
|
||||
subscribed_mailboxes,
|
||||
mailboxes,
|
||||
extra,
|
||||
manual_refresh,
|
||||
refresh_command: _,
|
||||
|
@ -359,16 +359,16 @@ impl FileSettings {
|
|||
let lowercase_format = format.to_lowercase();
|
||||
let s = AccountSettings {
|
||||
name: name.to_string(),
|
||||
root_folder,
|
||||
root_mailbox,
|
||||
format: format.clone(),
|
||||
identity,
|
||||
read_only,
|
||||
display_name,
|
||||
subscribed_folders,
|
||||
subscribed_mailboxes,
|
||||
manual_refresh,
|
||||
folders: folders
|
||||
mailboxes: mailboxes
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, v.folder_conf))
|
||||
.map(|(k, v)| (k, v.mailbox_conf))
|
||||
.collect(),
|
||||
extra,
|
||||
};
|
||||
|
@ -714,8 +714,8 @@ mod pp {
|
|||
let mut ret = pp_helper(&p_buf, 0)?;
|
||||
drop(p_buf);
|
||||
if let Ok(xdg_dirs) = xdg::BaseDirectories::with_prefix("meli") {
|
||||
for theme_folder in xdg_dirs.find_config_files("themes") {
|
||||
let read_dir = std::fs::read_dir(theme_folder)?;
|
||||
for theme_mailbox in xdg_dirs.find_config_files("themes") {
|
||||
let read_dir = std::fs::read_dir(theme_mailbox)?;
|
||||
for theme in read_dir {
|
||||
ret.extend(pp_helper(&theme?.path(), 0)?.chars());
|
||||
}
|
||||
|
|
|
@ -23,11 +23,11 @@
|
|||
* Account management from user configuration.
|
||||
*/
|
||||
|
||||
use super::{AccountConf, FileFolderConf};
|
||||
use super::{AccountConf, FileMailboxConf};
|
||||
use fnv::FnvHashMap;
|
||||
use melib::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
||||
use melib::backends::{
|
||||
BackendOp, Backends, Folder, FolderHash, MailBackend, NotifyFn, ReadOnlyOp, RefreshEvent,
|
||||
BackendOp, Backends, MailBackend, Mailbox, MailboxHash, NotifyFn, ReadOnlyOp, RefreshEvent,
|
||||
RefreshEventConsumer, RefreshEventKind, SpecialUsageMailbox,
|
||||
};
|
||||
use melib::email::*;
|
||||
|
@ -84,15 +84,15 @@ impl MailboxStatus {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FolderEntry {
|
||||
pub struct MailboxEntry {
|
||||
pub status: MailboxStatus,
|
||||
pub name: String,
|
||||
pub ref_folder: Folder,
|
||||
pub conf: FileFolderConf,
|
||||
pub ref_mailbox: Mailbox,
|
||||
pub conf: FileMailboxConf,
|
||||
pub worker: Worker,
|
||||
}
|
||||
|
||||
impl FolderEntry {
|
||||
impl MailboxEntry {
|
||||
pub fn status(&self) -> String {
|
||||
match self.status {
|
||||
MailboxStatus::Available => self.name().to_string(),
|
||||
|
@ -114,10 +114,10 @@ pub struct Account {
|
|||
pub index: usize,
|
||||
name: String,
|
||||
pub is_online: bool,
|
||||
pub(crate) folder_entries: FnvHashMap<FolderHash, FolderEntry>,
|
||||
pub(crate) folders_order: Vec<FolderHash>,
|
||||
tree: Vec<FolderNode>,
|
||||
sent_folder: Option<FolderHash>,
|
||||
pub(crate) mailbox_entries: FnvHashMap<MailboxHash, MailboxEntry>,
|
||||
pub(crate) mailboxes_order: Vec<MailboxHash>,
|
||||
tree: Vec<MailboxNode>,
|
||||
sent_mailbox: Option<MailboxHash>,
|
||||
pub(crate) collection: Collection,
|
||||
pub(crate) address_book: AddressBook,
|
||||
pub(crate) work_context: WorkContext,
|
||||
|
@ -126,7 +126,7 @@ pub struct Account {
|
|||
pub(crate) backend: Arc<RwLock<Box<dyn MailBackend>>>,
|
||||
|
||||
sender: Sender<ThreadEvent>,
|
||||
event_queue: VecDeque<(FolderHash, RefreshEvent)>,
|
||||
event_queue: VecDeque<(MailboxHash, RefreshEvent)>,
|
||||
notify_fn: Arc<NotifyFn>,
|
||||
}
|
||||
|
||||
|
@ -177,10 +177,10 @@ impl Drop for Account {
|
|||
}
|
||||
|
||||
#[derive(Serialize, Debug, Clone, Default)]
|
||||
pub struct FolderNode {
|
||||
pub hash: FolderHash,
|
||||
pub struct MailboxNode {
|
||||
pub hash: MailboxHash,
|
||||
pub depth: usize,
|
||||
pub children: Vec<FolderNode>,
|
||||
pub children: Vec<MailboxNode>,
|
||||
}
|
||||
|
||||
impl Account {
|
||||
|
@ -197,11 +197,11 @@ impl Account {
|
|||
let backend = map.get(settings.account().format())(
|
||||
settings.account(),
|
||||
Box::new(move |path: &str| {
|
||||
s.account.subscribed_folders.is_empty()
|
||||
|| (s.folder_confs.contains_key(path)
|
||||
&& s.folder_confs[path].folder_conf().subscribe.is_true())
|
||||
s.account.subscribed_mailboxes.is_empty()
|
||||
|| (s.mailbox_confs.contains_key(path)
|
||||
&& s.mailbox_confs[path].mailbox_conf().subscribe.is_true())
|
||||
|| s.account
|
||||
.subscribed_folders
|
||||
.subscribed_mailboxes
|
||||
.iter()
|
||||
.any(|m| path.matches_glob(m))
|
||||
}),
|
||||
|
@ -233,11 +233,11 @@ impl Account {
|
|||
index,
|
||||
name,
|
||||
is_online: false,
|
||||
folder_entries: Default::default(),
|
||||
folders_order: Default::default(),
|
||||
mailbox_entries: Default::default(),
|
||||
mailboxes_order: Default::default(),
|
||||
tree: Default::default(),
|
||||
address_book,
|
||||
sent_folder: Default::default(),
|
||||
sent_mailbox: Default::default(),
|
||||
collection: Default::default(),
|
||||
work_context,
|
||||
runtime_settings: settings.clone(),
|
||||
|
@ -251,22 +251,22 @@ impl Account {
|
|||
}
|
||||
|
||||
fn init(&mut self) {
|
||||
let mut ref_folders: FnvHashMap<FolderHash, Folder> =
|
||||
match self.backend.read().unwrap().folders() {
|
||||
let mut ref_mailboxes: FnvHashMap<MailboxHash, Mailbox> =
|
||||
match self.backend.read().unwrap().mailboxes() {
|
||||
Ok(f) => f,
|
||||
Err(err) => {
|
||||
debug!(&err);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut folder_entries: FnvHashMap<FolderHash, FolderEntry> =
|
||||
FnvHashMap::with_capacity_and_hasher(ref_folders.len(), Default::default());
|
||||
let mut folders_order: Vec<FolderHash> = Vec::with_capacity(ref_folders.len());
|
||||
let mut mailbox_entries: FnvHashMap<MailboxHash, MailboxEntry> =
|
||||
FnvHashMap::with_capacity_and_hasher(ref_mailboxes.len(), Default::default());
|
||||
let mut mailboxes_order: Vec<MailboxHash> = Vec::with_capacity(ref_mailboxes.len());
|
||||
|
||||
let mut sent_folder = None;
|
||||
for f in ref_folders.values_mut() {
|
||||
if let Some(conf) = self.settings.folder_confs.get_mut(f.path()) {
|
||||
conf.folder_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
|
||||
let mut sent_mailbox = None;
|
||||
for f in ref_mailboxes.values_mut() {
|
||||
if let Some(conf) = self.settings.mailbox_confs.get_mut(f.path()) {
|
||||
conf.mailbox_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
|
||||
Some(f.special_usage())
|
||||
} else {
|
||||
let tmp = SpecialUsageMailbox::detect_usage(f.name());
|
||||
|
@ -275,21 +275,21 @@ impl Account {
|
|||
}
|
||||
tmp
|
||||
};
|
||||
match conf.folder_conf.usage {
|
||||
match conf.mailbox_conf.usage {
|
||||
Some(SpecialUsageMailbox::Sent) => {
|
||||
sent_folder = Some(f.hash());
|
||||
sent_mailbox = Some(f.hash());
|
||||
}
|
||||
None => {
|
||||
if f.special_usage() == SpecialUsageMailbox::Sent {
|
||||
sent_folder = Some(f.hash());
|
||||
sent_mailbox = Some(f.hash());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
folder_entries.insert(
|
||||
mailbox_entries.insert(
|
||||
f.hash(),
|
||||
FolderEntry {
|
||||
ref_folder: f.clone(),
|
||||
MailboxEntry {
|
||||
ref_mailbox: f.clone(),
|
||||
name: f.path().to_string(),
|
||||
status: MailboxStatus::None,
|
||||
conf: conf.clone(),
|
||||
|
@ -297,8 +297,8 @@ impl Account {
|
|||
},
|
||||
);
|
||||
} else {
|
||||
let mut new = FileFolderConf::default();
|
||||
new.folder_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
|
||||
let mut new = FileMailboxConf::default();
|
||||
new.mailbox_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
|
||||
Some(f.special_usage())
|
||||
} else {
|
||||
let tmp = SpecialUsageMailbox::detect_usage(f.name());
|
||||
|
@ -307,14 +307,14 @@ impl Account {
|
|||
}
|
||||
tmp
|
||||
};
|
||||
if new.folder_conf.usage == Some(SpecialUsageMailbox::Sent) {
|
||||
sent_folder = Some(f.hash());
|
||||
if new.mailbox_conf.usage == Some(SpecialUsageMailbox::Sent) {
|
||||
sent_mailbox = Some(f.hash());
|
||||
}
|
||||
|
||||
folder_entries.insert(
|
||||
mailbox_entries.insert(
|
||||
f.hash(),
|
||||
FolderEntry {
|
||||
ref_folder: f.clone(),
|
||||
MailboxEntry {
|
||||
ref_mailbox: f.clone(),
|
||||
name: f.path().to_string(),
|
||||
status: MailboxStatus::None,
|
||||
conf: new,
|
||||
|
@ -324,14 +324,14 @@ impl Account {
|
|||
}
|
||||
}
|
||||
|
||||
let mut tree: Vec<FolderNode> = Vec::new();
|
||||
let mut tree: Vec<MailboxNode> = Vec::new();
|
||||
let mut collection: Collection = Collection::new(Default::default());
|
||||
for (h, f) in ref_folders.iter() {
|
||||
for (h, f) in ref_mailboxes.iter() {
|
||||
if !f.is_subscribed() {
|
||||
/* Skip unsubscribed folder */
|
||||
/* Skip unsubscribed mailbox */
|
||||
continue;
|
||||
}
|
||||
folder_entries.entry(*h).and_modify(|entry| {
|
||||
mailbox_entries.entry(*h).and_modify(|entry| {
|
||||
entry.status = MailboxStatus::Parsing(0, 0);
|
||||
entry.worker = Account::new_worker(
|
||||
f.clone(),
|
||||
|
@ -344,32 +344,32 @@ impl Account {
|
|||
collection.threads.insert(*h, Threads::default());
|
||||
}
|
||||
|
||||
build_folders_order(&mut tree, &folder_entries, &mut folders_order);
|
||||
self.folders_order = folders_order;
|
||||
self.folder_entries = folder_entries;
|
||||
build_mailboxes_order(&mut tree, &mailbox_entries, &mut mailboxes_order);
|
||||
self.mailboxes_order = mailboxes_order;
|
||||
self.mailbox_entries = mailbox_entries;
|
||||
self.tree = tree;
|
||||
self.sent_folder = sent_folder;
|
||||
self.sent_mailbox = sent_mailbox;
|
||||
self.collection = collection;
|
||||
}
|
||||
|
||||
fn new_worker(
|
||||
folder: Folder,
|
||||
mailbox: Mailbox,
|
||||
backend: &Arc<RwLock<Box<dyn MailBackend>>>,
|
||||
work_context: &WorkContext,
|
||||
notify_fn: Arc<NotifyFn>,
|
||||
) -> Worker {
|
||||
let mut mailbox_handle = backend.write().unwrap().get(&folder);
|
||||
let mut mailbox_handle = backend.write().unwrap().get(&mailbox);
|
||||
let mut builder = AsyncBuilder::new();
|
||||
let our_tx = builder.tx();
|
||||
let folder_hash = folder.hash();
|
||||
let priority = match folder.special_usage() {
|
||||
let mailbox_hash = mailbox.hash();
|
||||
let priority = match mailbox.special_usage() {
|
||||
SpecialUsageMailbox::Inbox => 0,
|
||||
SpecialUsageMailbox::Sent => 1,
|
||||
SpecialUsageMailbox::Drafts | SpecialUsageMailbox::Trash => 2,
|
||||
_ => {
|
||||
3 * folder
|
||||
3 * mailbox
|
||||
.path()
|
||||
.split(if folder.path().contains('/') {
|
||||
.split(if mailbox.path().contains('/') {
|
||||
'/'
|
||||
} else {
|
||||
'.'
|
||||
|
@ -385,7 +385,7 @@ impl Account {
|
|||
*/
|
||||
builder.set_priority(priority).set_is_static(true);
|
||||
let mut w = builder.build(Box::new(move |work_context| {
|
||||
let name = format!("Parsing {}", folder.path());
|
||||
let name = format!("Parsing {}", mailbox.path());
|
||||
let work = mailbox_handle.work().unwrap();
|
||||
work_context.new_work.send(work).unwrap();
|
||||
let thread_id = std::thread::current().id();
|
||||
|
@ -399,12 +399,12 @@ impl Account {
|
|||
match debug!(mailbox_handle.poll_block()) {
|
||||
Ok(s @ AsyncStatus::Payload(_)) => {
|
||||
our_tx.send(s).unwrap();
|
||||
debug!("notifying for {}", folder_hash);
|
||||
notify_fn.notify(folder_hash);
|
||||
debug!("notifying for {}", mailbox_hash);
|
||||
notify_fn.notify(mailbox_hash);
|
||||
}
|
||||
Ok(s @ AsyncStatus::Finished) => {
|
||||
our_tx.send(s).unwrap();
|
||||
notify_fn.notify(folder_hash);
|
||||
notify_fn.notify(mailbox_hash);
|
||||
debug!("exiting");
|
||||
work_context.finished.send(thread_id).unwrap();
|
||||
return;
|
||||
|
@ -424,15 +424,15 @@ impl Account {
|
|||
}
|
||||
Some(w)
|
||||
}
|
||||
pub fn reload(&mut self, event: RefreshEvent, folder_hash: FolderHash) -> Option<UIEvent> {
|
||||
if !self.folder_entries[&folder_hash].status.is_available() {
|
||||
self.event_queue.push_back((folder_hash, event));
|
||||
pub fn reload(&mut self, event: RefreshEvent, mailbox_hash: MailboxHash) -> Option<UIEvent> {
|
||||
if !self.mailbox_entries[&mailbox_hash].status.is_available() {
|
||||
self.event_queue.push_back((mailbox_hash, event));
|
||||
return None;
|
||||
}
|
||||
|
||||
let kind = event.kind();
|
||||
{
|
||||
//let mailbox: &mut Mailbox = self.folders[idx].as_mut().unwrap().as_mut().unwrap();
|
||||
//let mailbox: &mut Mailbox = self.mailboxes[idx].as_mut().unwrap().as_mut().unwrap();
|
||||
match kind {
|
||||
RefreshEventKind::Update(old_hash, envelope) => {
|
||||
#[cfg(feature = "sqlite3")]
|
||||
|
@ -450,12 +450,12 @@ impl Account {
|
|||
);
|
||||
}
|
||||
}
|
||||
self.collection.update(old_hash, *envelope, folder_hash);
|
||||
self.collection.update(old_hash, *envelope, mailbox_hash);
|
||||
return Some(EnvelopeUpdate(old_hash));
|
||||
}
|
||||
RefreshEventKind::Rename(old_hash, new_hash) => {
|
||||
debug!("rename {} to {}", old_hash, new_hash);
|
||||
self.collection.rename(old_hash, new_hash, folder_hash);
|
||||
self.collection.rename(old_hash, new_hash, mailbox_hash);
|
||||
#[cfg(feature = "sqlite3")]
|
||||
{
|
||||
let envelopes = self.collection.envelopes.read();
|
||||
|
@ -478,7 +478,7 @@ impl Account {
|
|||
RefreshEventKind::Create(envelope) => {
|
||||
let env_hash = envelope.hash();
|
||||
if self.collection.contains_key(&env_hash)
|
||||
&& self.collection[&folder_hash].contains(&env_hash)
|
||||
&& self.collection[&mailbox_hash].contains(&env_hash)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
@ -505,38 +505,30 @@ impl Account {
|
|||
);
|
||||
}
|
||||
}
|
||||
self.collection.insert(*envelope, folder_hash);
|
||||
if self
|
||||
.sent_folder
|
||||
.as_ref()
|
||||
.map(|h| *h == folder_hash)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
self.collection.insert_reply(env_hash);
|
||||
}
|
||||
self.collection.insert(*envelope, mailbox_hash);
|
||||
|
||||
if self.folder_entries[&folder_hash]
|
||||
if self.mailbox_entries[&mailbox_hash]
|
||||
.conf
|
||||
.folder_conf
|
||||
.mailbox_conf
|
||||
.ignore
|
||||
.is_true()
|
||||
{
|
||||
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
|
||||
return Some(UIEvent::MailboxUpdate((self.index, mailbox_hash)));
|
||||
}
|
||||
|
||||
let thread = {
|
||||
let thread_hash = self.collection.get_env(env_hash).thread();
|
||||
self.collection.threads[&folder_hash]
|
||||
.find_group(self.collection.threads[&folder_hash][&thread_hash].group)
|
||||
self.collection.threads[&mailbox_hash]
|
||||
.find_group(self.collection.threads[&mailbox_hash][&thread_hash].group)
|
||||
};
|
||||
if self.collection.threads[&folder_hash]
|
||||
if self.collection.threads[&mailbox_hash]
|
||||
.thread_ref(thread)
|
||||
.snoozed()
|
||||
{
|
||||
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
|
||||
return Some(UIEvent::MailboxUpdate((self.index, mailbox_hash)));
|
||||
}
|
||||
if is_seen || is_draft {
|
||||
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
|
||||
return Some(UIEvent::MailboxUpdate((self.index, mailbox_hash)));
|
||||
}
|
||||
|
||||
return Some(Notification(
|
||||
|
@ -545,7 +537,7 @@ impl Account {
|
|||
"{}\n{} {}",
|
||||
subject,
|
||||
self.name,
|
||||
self.folder_entries[&folder_hash].name()
|
||||
self.mailbox_entries[&mailbox_hash].name()
|
||||
),
|
||||
Some(crate::types::NotificationType::NewMail),
|
||||
));
|
||||
|
@ -567,17 +559,19 @@ impl Account {
|
|||
);
|
||||
}
|
||||
}
|
||||
self.collection.remove(envelope_hash, folder_hash);
|
||||
self.collection.remove(envelope_hash, mailbox_hash);
|
||||
return Some(EnvelopeRemove(envelope_hash));
|
||||
}
|
||||
RefreshEventKind::Rescan => {
|
||||
let handle = Account::new_worker(
|
||||
self.folder_entries[&folder_hash].ref_folder.clone(),
|
||||
self.mailbox_entries[&mailbox_hash].ref_mailbox.clone(),
|
||||
&mut self.backend,
|
||||
&self.work_context,
|
||||
self.notify_fn.clone(),
|
||||
);
|
||||
self.folder_entries.entry(folder_hash).and_modify(|entry| {
|
||||
self.mailbox_entries
|
||||
.entry(mailbox_hash)
|
||||
.and_modify(|entry| {
|
||||
entry.worker = handle;
|
||||
});
|
||||
}
|
||||
|
@ -599,7 +593,7 @@ impl Account {
|
|||
}
|
||||
None
|
||||
}
|
||||
pub fn refresh(&mut self, folder_hash: FolderHash) -> Result<()> {
|
||||
pub fn refresh(&mut self, mailbox_hash: MailboxHash) -> Result<()> {
|
||||
if let Some(ref refresh_command) = self.settings.conf().refresh_command {
|
||||
let parts = crate::split_command!(refresh_command);
|
||||
let (cmd, args) = (parts[0], &parts[1..]);
|
||||
|
@ -616,7 +610,7 @@ impl Account {
|
|||
let r = RefreshEventConsumer::new(Box::new(move |r| {
|
||||
sender_.send(ThreadEvent::from(r)).unwrap();
|
||||
}));
|
||||
let mut h = self.backend.write().unwrap().refresh(folder_hash, r)?;
|
||||
let mut h = self.backend.write().unwrap().refresh(mailbox_hash, r)?;
|
||||
self.work_context.new_work.send(h.work().unwrap()).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
@ -661,9 +655,9 @@ impl Account {
|
|||
self.tree.is_empty()
|
||||
}
|
||||
|
||||
pub fn list_folders(&self) -> Vec<FolderNode> {
|
||||
let mut ret = Vec::with_capacity(self.folder_entries.len());
|
||||
fn rec(node: &FolderNode, ret: &mut Vec<FolderNode>) {
|
||||
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>) {
|
||||
ret.push(node.clone());
|
||||
for c in node.children.iter() {
|
||||
rec(c, ret);
|
||||
|
@ -675,20 +669,22 @@ impl Account {
|
|||
ret
|
||||
}
|
||||
|
||||
pub fn folders_order(&self) -> &Vec<FolderHash> {
|
||||
&self.folders_order
|
||||
pub fn mailboxes_order(&self) -> &Vec<MailboxHash> {
|
||||
&self.mailboxes_order
|
||||
}
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn load_mailbox(&mut self, folder_hash: FolderHash, payload: Result<Vec<Envelope>>) {
|
||||
fn load_mailbox(&mut self, mailbox_hash: MailboxHash, payload: Result<Vec<Envelope>>) {
|
||||
if payload.is_err() {
|
||||
self.folder_entries.entry(folder_hash).and_modify(|entry| {
|
||||
self.mailbox_entries
|
||||
.entry(mailbox_hash)
|
||||
.and_modify(|entry| {
|
||||
entry.status = MailboxStatus::Failed(payload.unwrap_err());
|
||||
});
|
||||
self.sender
|
||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(folder_hash)))
|
||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(mailbox_hash)))
|
||||
.unwrap();
|
||||
return;
|
||||
}
|
||||
|
@ -697,37 +693,37 @@ impl Account {
|
|||
.into_iter()
|
||||
.map(|e| (e.hash(), e))
|
||||
.collect::<FnvHashMap<EnvelopeHash, Envelope>>();
|
||||
if let Some(updated_folders) =
|
||||
if let Some(updated_mailboxes) =
|
||||
self.collection
|
||||
.merge(envelopes, folder_hash, self.sent_folder)
|
||||
.merge(envelopes, mailbox_hash, self.sent_mailbox)
|
||||
{
|
||||
for f in updated_folders {
|
||||
for f in updated_mailboxes {
|
||||
self.sender
|
||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(f)))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
self.sender
|
||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(folder_hash)))
|
||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(mailbox_hash)))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn status(&mut self, folder_hash: FolderHash) -> result::Result<(), usize> {
|
||||
if folder_hash == 0 {
|
||||
pub fn status(&mut self, mailbox_hash: MailboxHash) -> result::Result<(), usize> {
|
||||
if mailbox_hash == 0 {
|
||||
return Err(0);
|
||||
}
|
||||
loop {
|
||||
match self
|
||||
.folder_entries
|
||||
.get_mut(&folder_hash)
|
||||
.mailbox_entries
|
||||
.get_mut(&mailbox_hash)
|
||||
.unwrap()
|
||||
.worker
|
||||
.as_mut()
|
||||
{
|
||||
None => {
|
||||
return if self.folder_entries[&folder_hash].status.is_available()
|
||||
|| (self.folder_entries[&folder_hash].status.is_parsing()
|
||||
&& self.collection.mailboxes.contains_key(&folder_hash))
|
||||
return if self.mailbox_entries[&mailbox_hash].status.is_available()
|
||||
|| (self.mailbox_entries[&mailbox_hash].status.is_parsing()
|
||||
&& self.collection.mailboxes.contains_key(&mailbox_hash))
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -739,30 +735,32 @@ impl Account {
|
|||
break;
|
||||
}
|
||||
Ok(AsyncStatus::Payload(envs)) => {
|
||||
debug!("got payload in status for {}", folder_hash);
|
||||
self.load_mailbox(folder_hash, envs);
|
||||
debug!("got payload in status for {}", mailbox_hash);
|
||||
self.load_mailbox(mailbox_hash, envs);
|
||||
}
|
||||
Ok(AsyncStatus::Finished) => {
|
||||
debug!("got finished in status for {}", folder_hash);
|
||||
self.folder_entries.entry(folder_hash).and_modify(|entry| {
|
||||
debug!("got finished in status for {}", mailbox_hash);
|
||||
self.mailbox_entries
|
||||
.entry(mailbox_hash)
|
||||
.and_modify(|entry| {
|
||||
entry.status = MailboxStatus::Available;
|
||||
entry.worker = None;
|
||||
});
|
||||
self.sender
|
||||
.send(ThreadEvent::UIEvent(UIEvent::MailboxUpdate((
|
||||
self.index,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
))))
|
||||
.unwrap();
|
||||
}
|
||||
Ok(AsyncStatus::ProgressReport(n)) => {
|
||||
self.folder_entries.entry(folder_hash).and_modify(|entry| {
|
||||
match entry.status {
|
||||
self.mailbox_entries
|
||||
.entry(mailbox_hash)
|
||||
.and_modify(|entry| match entry.status {
|
||||
MailboxStatus::Parsing(ref mut d, _) => {
|
||||
*d += n;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
//return Err(n);
|
||||
}
|
||||
|
@ -772,9 +770,9 @@ impl Account {
|
|||
},
|
||||
};
|
||||
}
|
||||
if self.folder_entries[&folder_hash].status.is_available()
|
||||
|| (self.folder_entries[&folder_hash].status.is_parsing()
|
||||
&& self.collection.mailboxes.contains_key(&folder_hash))
|
||||
if self.mailbox_entries[&mailbox_hash].status.is_available()
|
||||
|| (self.mailbox_entries[&mailbox_hash].status.is_parsing()
|
||||
&& self.collection.mailboxes.contains_key(&mailbox_hash))
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -785,23 +783,27 @@ impl Account {
|
|||
pub fn save_special(
|
||||
&self,
|
||||
bytes: &[u8],
|
||||
folder_type: SpecialUsageMailbox,
|
||||
mailbox_type: SpecialUsageMailbox,
|
||||
flags: Flag,
|
||||
) -> Result<()> {
|
||||
let mut failure = true;
|
||||
for folder in &[
|
||||
self.special_use_folder(folder_type),
|
||||
self.special_use_folder(SpecialUsageMailbox::Inbox),
|
||||
self.special_use_folder(SpecialUsageMailbox::Normal),
|
||||
for mailbox in &[
|
||||
self.special_use_mailbox(mailbox_type),
|
||||
self.special_use_mailbox(SpecialUsageMailbox::Inbox),
|
||||
self.special_use_mailbox(SpecialUsageMailbox::Normal),
|
||||
] {
|
||||
if folder.is_none() {
|
||||
if mailbox.is_none() {
|
||||
continue;
|
||||
}
|
||||
let folder = folder.unwrap();
|
||||
if let Err(e) = self.save(bytes, folder, Some(flags)) {
|
||||
let mailbox = mailbox.unwrap();
|
||||
if let Err(e) = self.save(bytes, mailbox, Some(flags)) {
|
||||
debug!("{:?} could not save msg", e);
|
||||
melib::log(
|
||||
format!("Could not save in '{}' folder: {}.", folder, e.to_string()),
|
||||
format!(
|
||||
"Could not save in '{}' mailbox: {}.",
|
||||
mailbox,
|
||||
e.to_string()
|
||||
),
|
||||
melib::ERROR,
|
||||
);
|
||||
} else {
|
||||
|
@ -824,19 +826,19 @@ impl Account {
|
|||
"Message was stored in {} so that you can restore it manually.",
|
||||
file.path.display()
|
||||
))
|
||||
.set_summary("Could not save in any folder"));
|
||||
.set_summary("Could not save in any mailbox"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn save(&self, bytes: &[u8], folder: &str, flags: Option<Flag>) -> Result<()> {
|
||||
pub fn save(&self, bytes: &[u8], mailbox: &str, flags: Option<Flag>) -> Result<()> {
|
||||
if self.settings.account.read_only() {
|
||||
return Err(MeliError::new(format!(
|
||||
"Account {} is read-only.",
|
||||
self.name.as_str()
|
||||
)));
|
||||
}
|
||||
self.backend.write().unwrap().save(bytes, folder, flags)
|
||||
self.backend.write().unwrap().save(bytes, mailbox, flags)
|
||||
}
|
||||
|
||||
pub fn contains_key(&self, h: EnvelopeHash) -> bool {
|
||||
|
@ -851,127 +853,132 @@ impl Account {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn thread(&self, h: ThreadNodeHash, f: FolderHash) -> &ThreadNode {
|
||||
pub fn thread(&self, h: ThreadNodeHash, f: MailboxHash) -> &ThreadNode {
|
||||
&self.collection.threads[&f].thread_nodes()[&h]
|
||||
}
|
||||
|
||||
pub fn folder_operation(
|
||||
pub fn mailbox_operation(
|
||||
&mut self,
|
||||
op: crate::execute::actions::FolderOperation,
|
||||
op: crate::execute::actions::MailboxOperation,
|
||||
) -> Result<String> {
|
||||
use crate::execute::actions::FolderOperation;
|
||||
use crate::execute::actions::MailboxOperation;
|
||||
if self.settings.account.read_only() {
|
||||
return Err(MeliError::new("Account is read-only."));
|
||||
}
|
||||
match op {
|
||||
FolderOperation::Create(path) => {
|
||||
let (folder_hash, mut folders) = self
|
||||
MailboxOperation::Create(path) => {
|
||||
let (mailbox_hash, mut mailboxes) = self
|
||||
.backend
|
||||
.write()
|
||||
.unwrap()
|
||||
.create_folder(path.to_string())?;
|
||||
.create_mailbox(path.to_string())?;
|
||||
self.sender
|
||||
.send(ThreadEvent::UIEvent(UIEvent::MailboxCreate((
|
||||
self.index,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
))))
|
||||
.unwrap();
|
||||
let mut new = FileFolderConf::default();
|
||||
new.folder_conf.subscribe = super::ToggleFlag::InternalVal(true);
|
||||
new.folder_conf.usage =
|
||||
if folders[&folder_hash].special_usage() != SpecialUsageMailbox::Normal {
|
||||
Some(folders[&folder_hash].special_usage())
|
||||
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(folders[&folder_hash].name());
|
||||
let tmp = SpecialUsageMailbox::detect_usage(mailboxes[&mailbox_hash].name());
|
||||
if tmp != Some(SpecialUsageMailbox::Normal) && tmp != None {
|
||||
folders.entry(folder_hash).and_modify(|entry| {
|
||||
mailboxes.entry(mailbox_hash).and_modify(|entry| {
|
||||
let _ = entry.set_special_usage(tmp.unwrap());
|
||||
});
|
||||
}
|
||||
tmp
|
||||
};
|
||||
/* if new folder has parent, we need to update its children field */
|
||||
if let Some(parent_hash) = folders[&folder_hash].parent() {
|
||||
self.folder_entries.entry(parent_hash).and_modify(|parent| {
|
||||
parent.ref_folder = folders.remove(&parent_hash).unwrap();
|
||||
/* if new mailbox has parent, we need to update its children field */
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
self.folder_entries.insert(
|
||||
folder_hash,
|
||||
FolderEntry {
|
||||
name: folders[&folder_hash].path().to_string(),
|
||||
self.mailbox_entries.insert(
|
||||
mailbox_hash,
|
||||
MailboxEntry {
|
||||
name: mailboxes[&mailbox_hash].path().to_string(),
|
||||
status: MailboxStatus::Parsing(0, 0),
|
||||
conf: new,
|
||||
worker: Account::new_worker(
|
||||
folders[&folder_hash].clone(),
|
||||
mailboxes[&mailbox_hash].clone(),
|
||||
&mut self.backend,
|
||||
&self.work_context,
|
||||
self.notify_fn.clone(),
|
||||
),
|
||||
ref_folder: folders.remove(&folder_hash).unwrap(),
|
||||
ref_mailbox: mailboxes.remove(&mailbox_hash).unwrap(),
|
||||
},
|
||||
);
|
||||
self.collection
|
||||
.threads
|
||||
.insert(folder_hash, Threads::default());
|
||||
.insert(mailbox_hash, Threads::default());
|
||||
self.collection
|
||||
.mailboxes
|
||||
.insert(folder_hash, Default::default());
|
||||
build_folders_order(
|
||||
.insert(mailbox_hash, Default::default());
|
||||
build_mailboxes_order(
|
||||
&mut self.tree,
|
||||
&self.folder_entries,
|
||||
&mut self.folders_order,
|
||||
&self.mailbox_entries,
|
||||
&mut self.mailboxes_order,
|
||||
);
|
||||
Ok(format!("`{}` successfully created.", &path))
|
||||
}
|
||||
FolderOperation::Delete(path) => {
|
||||
if self.folder_entries.len() == 1 {
|
||||
MailboxOperation::Delete(path) => {
|
||||
if self.mailbox_entries.len() == 1 {
|
||||
return Err(MeliError::new("Cannot delete only mailbox."));
|
||||
}
|
||||
let folder_hash = if let Some((folder_hash, _)) = self
|
||||
.folder_entries
|
||||
let mailbox_hash = if let Some((mailbox_hash, _)) = self
|
||||
.mailbox_entries
|
||||
.iter()
|
||||
.find(|(_, f)| f.ref_folder.path() == path)
|
||||
.find(|(_, f)| f.ref_mailbox.path() == path)
|
||||
{
|
||||
*folder_hash
|
||||
*mailbox_hash
|
||||
} else {
|
||||
return Err(MeliError::new("Mailbox with that path not found."));
|
||||
};
|
||||
let mut folders = self.backend.write().unwrap().delete_folder(folder_hash)?;
|
||||
let mut mailboxes = self.backend.write().unwrap().delete_mailbox(mailbox_hash)?;
|
||||
self.sender
|
||||
.send(ThreadEvent::UIEvent(UIEvent::MailboxDelete((
|
||||
self.index,
|
||||
folder_hash,
|
||||
mailbox_hash,
|
||||
))))
|
||||
.unwrap();
|
||||
if let Some(pos) = self.folders_order.iter().position(|&h| h == folder_hash) {
|
||||
self.folders_order.remove(pos);
|
||||
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 == folder_hash) {
|
||||
if let Some(pos) = self.tree.iter().position(|n| n.hash == mailbox_hash) {
|
||||
self.tree.remove(pos);
|
||||
}
|
||||
if self.sent_folder == Some(folder_hash) {
|
||||
self.sent_folder = None;
|
||||
if self.sent_mailbox == Some(mailbox_hash) {
|
||||
self.sent_mailbox = None;
|
||||
}
|
||||
self.collection.threads.remove(&folder_hash);
|
||||
/* if deleted folder had parent, we need to update its children field */
|
||||
self.collection.threads.remove(&mailbox_hash);
|
||||
/* if deleted mailbox had parent, we need to update its children field */
|
||||
if let Some(parent_hash) = self
|
||||
.folder_entries
|
||||
.remove(&folder_hash)
|
||||
.mailbox_entries
|
||||
.remove(&mailbox_hash)
|
||||
.unwrap()
|
||||
.ref_folder
|
||||
.ref_mailbox
|
||||
.parent()
|
||||
{
|
||||
self.folder_entries.entry(parent_hash).and_modify(|parent| {
|
||||
parent.ref_folder = folders.remove(&parent_hash).unwrap();
|
||||
self.mailbox_entries
|
||||
.entry(parent_hash)
|
||||
.and_modify(|parent| {
|
||||
parent.ref_mailbox = mailboxes.remove(&parent_hash).unwrap();
|
||||
});
|
||||
}
|
||||
self.collection.mailboxes.remove(&folder_hash);
|
||||
build_folders_order(
|
||||
self.collection.mailboxes.remove(&mailbox_hash);
|
||||
build_mailboxes_order(
|
||||
&mut self.tree,
|
||||
&self.folder_entries,
|
||||
&mut self.folders_order,
|
||||
&self.mailbox_entries,
|
||||
&mut self.mailboxes_order,
|
||||
);
|
||||
// FIXME Kill worker as well
|
||||
|
||||
|
@ -979,18 +986,18 @@ impl Account {
|
|||
|
||||
Ok(format!("'`{}` has been deleted.", &path))
|
||||
}
|
||||
FolderOperation::Subscribe(_) => Err(MeliError::new("Not implemented.")),
|
||||
FolderOperation::Unsubscribe(_) => Err(MeliError::new("Not implemented.")),
|
||||
FolderOperation::Rename(_, _) => Err(MeliError::new("Not implemented.")),
|
||||
FolderOperation::SetPermissions(_) => Err(MeliError::new("Not implemented.")),
|
||||
MailboxOperation::Subscribe(_) => Err(MeliError::new("Not implemented.")),
|
||||
MailboxOperation::Unsubscribe(_) => Err(MeliError::new("Not implemented.")),
|
||||
MailboxOperation::Rename(_, _) => Err(MeliError::new("Not implemented.")),
|
||||
MailboxOperation::SetPermissions(_) => Err(MeliError::new("Not implemented.")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn special_use_folder(&self, special_use: SpecialUsageMailbox) -> Option<&str> {
|
||||
pub fn special_use_mailbox(&self, special_use: SpecialUsageMailbox) -> Option<&str> {
|
||||
let ret = self
|
||||
.folder_entries
|
||||
.mailbox_entries
|
||||
.iter()
|
||||
.find(|(_, f)| f.conf.folder_conf().usage == Some(special_use));
|
||||
.find(|(_, f)| f.conf.mailbox_conf().usage == Some(special_use));
|
||||
if let Some(ret) = ret.as_ref() {
|
||||
Some(ret.1.name())
|
||||
} else {
|
||||
|
@ -1017,10 +1024,10 @@ impl Account {
|
|||
&self,
|
||||
search_term: &str,
|
||||
sort: (SortField, SortOrder),
|
||||
folder_hash: FolderHash,
|
||||
mailbox_hash: MailboxHash,
|
||||
) -> Result<SmallVec<[EnvelopeHash; 512]>> {
|
||||
if self.settings.account().format() == "imap" {
|
||||
return crate::cache::imap_search(search_term, sort, folder_hash, &self.backend);
|
||||
return crate::cache::imap_search(search_term, sort, mailbox_hash, &self.backend);
|
||||
}
|
||||
|
||||
#[cfg(feature = "notmuch")]
|
||||
|
@ -1049,7 +1056,7 @@ impl Account {
|
|||
let mut ret = SmallVec::new();
|
||||
let envelopes = self.collection.envelopes.read().unwrap();
|
||||
|
||||
for &env_hash in &self.collection[&folder_hash].iter() {
|
||||
for &env_hash in &self.collection[&mailbox_hash].iter() {
|
||||
let envelope = &envelopes[&env_hash];
|
||||
if envelope.subject().contains(&search_term) {
|
||||
ret.push(env_hash);
|
||||
|
@ -1072,97 +1079,97 @@ impl Account {
|
|||
}
|
||||
}
|
||||
|
||||
impl Index<&FolderHash> for Account {
|
||||
type Output = FolderEntry;
|
||||
fn index(&self, index: &FolderHash) -> &FolderEntry {
|
||||
&self.folder_entries[index]
|
||||
impl Index<&MailboxHash> for Account {
|
||||
type Output = MailboxEntry;
|
||||
fn index(&self, index: &MailboxHash) -> &MailboxEntry {
|
||||
&self.mailbox_entries[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<&FolderHash> for Account {
|
||||
fn index_mut(&mut self, index: &FolderHash) -> &mut FolderEntry {
|
||||
self.folder_entries.get_mut(index).unwrap()
|
||||
impl IndexMut<&MailboxHash> for Account {
|
||||
fn index_mut(&mut self, index: &MailboxHash) -> &mut MailboxEntry {
|
||||
self.mailbox_entries.get_mut(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn build_folders_order(
|
||||
tree: &mut Vec<FolderNode>,
|
||||
folder_entries: &FnvHashMap<FolderHash, FolderEntry>,
|
||||
folders_order: &mut Vec<FolderHash>,
|
||||
fn build_mailboxes_order(
|
||||
tree: &mut Vec<MailboxNode>,
|
||||
mailbox_entries: &FnvHashMap<MailboxHash, MailboxEntry>,
|
||||
mailboxes_order: &mut Vec<MailboxHash>,
|
||||
) {
|
||||
tree.clear();
|
||||
folders_order.clear();
|
||||
for (h, f) in folder_entries.iter() {
|
||||
if f.ref_folder.parent().is_none() {
|
||||
mailboxes_order.clear();
|
||||
for (h, f) in mailbox_entries.iter() {
|
||||
if f.ref_mailbox.parent().is_none() {
|
||||
fn rec(
|
||||
h: FolderHash,
|
||||
folder_entries: &FnvHashMap<FolderHash, FolderEntry>,
|
||||
h: MailboxHash,
|
||||
mailbox_entries: &FnvHashMap<MailboxHash, MailboxEntry>,
|
||||
depth: usize,
|
||||
) -> FolderNode {
|
||||
let mut node = FolderNode {
|
||||
) -> MailboxNode {
|
||||
let mut node = MailboxNode {
|
||||
hash: h,
|
||||
children: Vec::new(),
|
||||
depth,
|
||||
};
|
||||
for &c in folder_entries[&h].ref_folder.children() {
|
||||
if folder_entries.contains_key(&c) {
|
||||
node.children.push(rec(c, folder_entries, depth + 1));
|
||||
for &c in mailbox_entries[&h].ref_mailbox.children() {
|
||||
if mailbox_entries.contains_key(&c) {
|
||||
node.children.push(rec(c, mailbox_entries, depth + 1));
|
||||
}
|
||||
}
|
||||
node
|
||||
};
|
||||
|
||||
tree.push(rec(*h, &folder_entries, 0));
|
||||
tree.push(rec(*h, &mailbox_entries, 0));
|
||||
}
|
||||
}
|
||||
|
||||
tree.sort_unstable_by(|a, b| {
|
||||
if folder_entries[&b.hash]
|
||||
.ref_folder
|
||||
if mailbox_entries[&b.hash]
|
||||
.ref_mailbox
|
||||
.path()
|
||||
.eq_ignore_ascii_case("INBOX")
|
||||
{
|
||||
std::cmp::Ordering::Greater
|
||||
} else if folder_entries[&a.hash]
|
||||
.ref_folder
|
||||
} else if mailbox_entries[&a.hash]
|
||||
.ref_mailbox
|
||||
.path()
|
||||
.eq_ignore_ascii_case("INBOX")
|
||||
{
|
||||
std::cmp::Ordering::Less
|
||||
} else {
|
||||
folder_entries[&a.hash]
|
||||
.ref_folder
|
||||
mailbox_entries[&a.hash]
|
||||
.ref_mailbox
|
||||
.path()
|
||||
.cmp(&folder_entries[&b.hash].ref_folder.path())
|
||||
.cmp(&mailbox_entries[&b.hash].ref_mailbox.path())
|
||||
}
|
||||
});
|
||||
|
||||
let mut stack: SmallVec<[Option<&FolderNode>; 16]> = SmallVec::new();
|
||||
let mut stack: SmallVec<[Option<&MailboxNode>; 16]> = SmallVec::new();
|
||||
for n in tree.iter_mut() {
|
||||
folders_order.push(n.hash);
|
||||
mailboxes_order.push(n.hash);
|
||||
n.children.sort_unstable_by(|a, b| {
|
||||
if folder_entries[&b.hash]
|
||||
.ref_folder
|
||||
if mailbox_entries[&b.hash]
|
||||
.ref_mailbox
|
||||
.path()
|
||||
.eq_ignore_ascii_case("INBOX")
|
||||
{
|
||||
std::cmp::Ordering::Greater
|
||||
} else if folder_entries[&a.hash]
|
||||
.ref_folder
|
||||
} else if mailbox_entries[&a.hash]
|
||||
.ref_mailbox
|
||||
.path()
|
||||
.eq_ignore_ascii_case("INBOX")
|
||||
{
|
||||
std::cmp::Ordering::Less
|
||||
} else {
|
||||
folder_entries[&a.hash]
|
||||
.ref_folder
|
||||
mailbox_entries[&a.hash]
|
||||
.ref_mailbox
|
||||
.path()
|
||||
.cmp(&folder_entries[&b.hash].ref_folder.path())
|
||||
.cmp(&mailbox_entries[&b.hash].ref_mailbox.path())
|
||||
}
|
||||
});
|
||||
stack.extend(n.children.iter().rev().map(Some));
|
||||
while let Some(Some(next)) = stack.pop() {
|
||||
folders_order.push(next.hash);
|
||||
mailboxes_order.push(next.hash);
|
||||
stack.extend(next.children.iter().rev().map(Some));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,13 +109,13 @@ shortcut_key_values! { "listing",
|
|||
scroll_down |> "Scroll down list." |> Key::Down,
|
||||
new_mail |> "Start new mail draft in new tab." |> Key::Char('m'),
|
||||
next_account |> "Go to next account." |> Key::Char('h'),
|
||||
next_folder |> "Go to next folder." |> Key::Char('J'),
|
||||
next_mailbox |> "Go to next mailbox." |> Key::Char('J'),
|
||||
next_page |> "Go to next page." |> Key::PageDown,
|
||||
prev_account |> "Go to previous account." |> Key::Char('l'),
|
||||
prev_folder |> "Go to previous folder." |> Key::Char('K'),
|
||||
prev_mailbox |> "Go to previous mailbox." |> Key::Char('K'),
|
||||
prev_page |> "Go to previous page." |> Key::PageUp,
|
||||
search |> "Search within list of e-mails." |> Key::Char('/'),
|
||||
refresh |> "Manually request a folder refresh." |> Key::F(5),
|
||||
refresh |> "Manually request a mailbox refresh." |> Key::F(5),
|
||||
set_seen |> "Set thread as seen." |> Key::Char('n'),
|
||||
toggle_menu_visibility |> "Toggle visibility of side menu in mail list." |> Key::Char('`')
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ pub use melib::thread::{SortField, SortOrder};
|
|||
use nom::{digit, not_line_ending, IResult};
|
||||
use std;
|
||||
pub mod actions;
|
||||
use actions::FolderOperation;
|
||||
use actions::MailboxOperation;
|
||||
pub mod history;
|
||||
pub use crate::actions::AccountAction::{self, *};
|
||||
pub use crate::actions::Action::{self, *};
|
||||
|
@ -247,74 +247,74 @@ define_commands!([
|
|||
);
|
||||
)
|
||||
},
|
||||
{ tags: ["create-folder "],
|
||||
desc: "create-folder ACCOUNT FOLDER_PATH",
|
||||
{ tags: ["create-mailbox "],
|
||||
desc: "create-mailbox ACCOUNT MAILBOX_PATH",
|
||||
parser:(
|
||||
named!( create_folder<Action>,
|
||||
named!( create_mailbox<Action>,
|
||||
do_parse!(
|
||||
ws!(tag!("create-folder"))
|
||||
ws!(tag!("create-mailbox"))
|
||||
>> account: quoted_argument
|
||||
>> is_a!(" ")
|
||||
>> path: quoted_argument
|
||||
>> (Folder(account.to_string(), FolderOperation::Create(path.to_string())))
|
||||
>> (Mailbox(account.to_string(), MailboxOperation::Create(path.to_string())))
|
||||
)
|
||||
);
|
||||
)
|
||||
},
|
||||
{ tags: ["subscribe-folder "],
|
||||
desc: "subscribe-folder ACCOUNT FOLDER_PATH",
|
||||
{ tags: ["subscribe-mailbox "],
|
||||
desc: "subscribe-mailbox ACCOUNT MAILBOX_PATH",
|
||||
parser:(
|
||||
named!( sub_folder<Action>,
|
||||
named!( sub_mailbox<Action>,
|
||||
do_parse!(
|
||||
ws!(tag!("subscribe-folder"))
|
||||
ws!(tag!("subscribe-mailbox"))
|
||||
>> account: quoted_argument
|
||||
>> is_a!(" ")
|
||||
>> path: quoted_argument
|
||||
>> (Folder(account.to_string(), FolderOperation::Subscribe(path.to_string())))
|
||||
>> (Mailbox(account.to_string(), MailboxOperation::Subscribe(path.to_string())))
|
||||
)
|
||||
);
|
||||
)
|
||||
},
|
||||
{ tags: ["unsubscribe-folder "],
|
||||
desc: "unsubscribe-folder ACCOUNT FOLDER_PATH",
|
||||
{ tags: ["unsubscribe-mailbox "],
|
||||
desc: "unsubscribe-mailbox ACCOUNT MAILBOX_PATH",
|
||||
parser:(
|
||||
named!( unsub_folder<Action>,
|
||||
named!( unsub_mailbox<Action>,
|
||||
do_parse!(
|
||||
ws!(tag!("unsubscribe-folder"))
|
||||
ws!(tag!("unsubscribe-mailbox"))
|
||||
>> account: quoted_argument
|
||||
>> is_a!(" ")
|
||||
>> path: quoted_argument
|
||||
>> (Folder(account.to_string(), FolderOperation::Unsubscribe(path.to_string())))
|
||||
>> (Mailbox(account.to_string(), MailboxOperation::Unsubscribe(path.to_string())))
|
||||
)
|
||||
);
|
||||
)
|
||||
},
|
||||
{ tags: ["rename-folder "],
|
||||
desc: "rename-folder ACCOUNT FOLDER_PATH_SRC FOLDER_PATH_DEST",
|
||||
{ tags: ["rename-mailbox "],
|
||||
desc: "rename-mailbox ACCOUNT MAILBOX_PATH_SRC MAILBOX_PATH_DEST",
|
||||
parser:(
|
||||
named!( rename_folder<Action>,
|
||||
named!( rename_mailbox<Action>,
|
||||
do_parse!(
|
||||
ws!(tag!("rename-folder"))
|
||||
ws!(tag!("rename-mailbox"))
|
||||
>> account: quoted_argument
|
||||
>> is_a!(" ")
|
||||
>> src: quoted_argument
|
||||
>> is_a!(" ")
|
||||
>> dest: quoted_argument
|
||||
>> (Folder(account.to_string(), FolderOperation::Rename(src.to_string(), dest.to_string())))
|
||||
>> (Mailbox(account.to_string(), MailboxOperation::Rename(src.to_string(), dest.to_string())))
|
||||
)
|
||||
);
|
||||
)
|
||||
},
|
||||
{ tags: ["delete-folder "],
|
||||
desc: "delete-folder ACCOUNT FOLDER_PATH",
|
||||
{ tags: ["delete-mailbox "],
|
||||
desc: "delete-mailbox ACCOUNT MAILBOX_PATH",
|
||||
parser:(
|
||||
named!( delete_folder<Action>,
|
||||
named!( delete_mailbox<Action>,
|
||||
do_parse!(
|
||||
ws!(tag!("delete-folder"))
|
||||
ws!(tag!("delete-mailbox"))
|
||||
>> account: quoted_argument
|
||||
>> is_a!(" ")
|
||||
>> path: quoted_argument
|
||||
>> (Folder(account.to_string(), FolderOperation::Delete(path.to_string())))
|
||||
>> (Mailbox(account.to_string(), MailboxOperation::Delete(path.to_string())))
|
||||
)
|
||||
);
|
||||
)
|
||||
|
@ -437,5 +437,5 @@ named!(account_action<Action>, alt_complete!(reindex));
|
|||
named!(view<Action>, alt_complete!(pipe | save_attachment));
|
||||
|
||||
named!(pub parse_command<Action>,
|
||||
alt_complete!( goto | listing_action | sort | subsort | close | mailinglist | setenv | printenv | view | compose_action | create_folder | sub_folder | unsub_folder | delete_folder | rename_folder | account_action )
|
||||
alt_complete!( goto | listing_action | sort | subsort | close | mailinglist | setenv | printenv | view | compose_action | create_mailbox | sub_mailbox | unsub_mailbox | delete_mailbox | rename_mailbox | account_action )
|
||||
);
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
*/
|
||||
|
||||
use crate::components::Component;
|
||||
use melib::backends::FolderHash;
|
||||
use melib::backends::MailboxHash;
|
||||
pub use melib::thread::{SortField, SortOrder};
|
||||
use melib::{Draft, EnvelopeHash};
|
||||
|
||||
|
@ -55,7 +55,7 @@ pub enum ListingAction {
|
|||
pub enum TabAction {
|
||||
New(Option<Box<dyn Component>>),
|
||||
NewDraft(usize, Option<Draft>),
|
||||
Reply((usize, FolderHash), EnvelopeHash), // thread coordinates (account, mailbox) and envelope
|
||||
Reply((usize, MailboxHash), EnvelopeHash), // thread coordinates (account, mailbox) and envelope
|
||||
Close,
|
||||
Edit(usize, EnvelopeHash), // account_position, envelope hash
|
||||
Kill(Uuid),
|
||||
|
@ -87,14 +87,14 @@ pub enum AccountAction {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FolderOperation {
|
||||
Create(NewFolderPath),
|
||||
Delete(FolderPath),
|
||||
Subscribe(FolderPath),
|
||||
Unsubscribe(FolderPath),
|
||||
Rename(FolderPath, NewFolderPath),
|
||||
pub enum MailboxOperation {
|
||||
Create(NewMailboxPath),
|
||||
Delete(MailboxPath),
|
||||
Subscribe(MailboxPath),
|
||||
Unsubscribe(MailboxPath),
|
||||
Rename(MailboxPath, NewMailboxPath),
|
||||
// Placeholder
|
||||
SetPermissions(FolderPath),
|
||||
SetPermissions(MailboxPath),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -110,7 +110,7 @@ pub enum Action {
|
|||
SetEnv(String, String),
|
||||
PrintEnv(String),
|
||||
Compose(ComposeAction),
|
||||
Folder(AccountName, FolderOperation),
|
||||
Mailbox(AccountName, MailboxOperation),
|
||||
AccountAction(AccountName, AccountAction),
|
||||
}
|
||||
|
||||
|
@ -128,12 +128,12 @@ impl Action {
|
|||
Action::SetEnv(_, _) => false,
|
||||
Action::PrintEnv(_) => false,
|
||||
Action::Compose(_) => false,
|
||||
Action::Folder(_, _) => true,
|
||||
Action::Mailbox(_, _) => true,
|
||||
Action::AccountAction(_, _) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type AccountName = String;
|
||||
type FolderPath = String;
|
||||
type NewFolderPath = String;
|
||||
type MailboxPath = String;
|
||||
type NewMailboxPath = String;
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
use super::*;
|
||||
use fnv::FnvHashMap;
|
||||
use melib::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
||||
use melib::backends::FolderHash;
|
||||
use melib::backends::{Backend, BackendOp, Backends, Folder, MailBackend, RefreshEventConsumer};
|
||||
use melib::backends::MailboxHash;
|
||||
use melib::backends::{Backend, BackendOp, Backends, MailBackend, Mailbox, RefreshEventConsumer};
|
||||
use melib::conf::AccountSettings;
|
||||
use melib::email::{Envelope, EnvelopeHash, Flag};
|
||||
use melib::error::{MeliError, Result};
|
||||
|
@ -86,9 +86,9 @@ impl MailBackend for PluginBackend {
|
|||
|
||||
fn connect(&mut self) {}
|
||||
|
||||
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||
fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
|
||||
let mut w = AsyncBuilder::new();
|
||||
let _folder_hash = folder.hash();
|
||||
let _mailbox_hash = mailbox.hash();
|
||||
let channel = self.channel.clone();
|
||||
let handle = {
|
||||
let tx = w.tx();
|
||||
|
@ -180,7 +180,7 @@ impl MailBackend for PluginBackend {
|
|||
|
||||
fn refresh(
|
||||
&mut self,
|
||||
_folder_hash: FolderHash,
|
||||
_mailbox_hash: MailboxHash,
|
||||
_sender: RefreshEventConsumer,
|
||||
) -> Result<Async<()>> {
|
||||
Err(MeliError::new("Unimplemented."))
|
||||
|
@ -193,9 +193,9 @@ impl MailBackend for PluginBackend {
|
|||
Err(MeliError::new("Unimplemented."))
|
||||
}
|
||||
|
||||
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
|
||||
let mut ret: FnvHashMap<FolderHash, Folder> = Default::default();
|
||||
ret.insert(0, Folder::default());
|
||||
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||
let mut ret: FnvHashMap<MailboxHash, Mailbox> = Default::default();
|
||||
ret.insert(0, Mailbox::default());
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
|
@ -208,13 +208,13 @@ impl MailBackend for PluginBackend {
|
|||
})
|
||||
}
|
||||
|
||||
fn save(&self, _bytes: &[u8], _folder: &str, _flags: Option<Flag>) -> Result<()> {
|
||||
fn save(&self, _bytes: &[u8], _mailbox: &str, _flags: Option<Flag>) -> Result<()> {
|
||||
Err(MeliError::new("Unimplemented."))
|
||||
}
|
||||
fn create_folder(
|
||||
fn create_mailbox(
|
||||
&mut self,
|
||||
_name: String,
|
||||
) -> Result<(FolderHash, FnvHashMap<FolderHash, Folder>)> {
|
||||
) -> Result<(MailboxHash, FnvHashMap<MailboxHash, Mailbox>)> {
|
||||
Err(MeliError::new("Unimplemented."))
|
||||
}
|
||||
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
|
||||
|
|
36
src/state.rs
36
src/state.rs
|
@ -30,7 +30,7 @@ Input is received in the main loop from threads which listen on the stdin for us
|
|||
|
||||
use super::*;
|
||||
use crate::plugins::PluginManager;
|
||||
use melib::backends::{FolderHash, NotifyFn};
|
||||
use melib::backends::{MailboxHash, NotifyFn};
|
||||
|
||||
use crossbeam::channel::{unbounded, Receiver, Sender};
|
||||
use fnv::FnvHashMap;
|
||||
|
@ -85,7 +85,7 @@ impl InputHandler {
|
|||
/// A context container for loaded settings, accounts, UI changes, etc.
|
||||
pub struct Context {
|
||||
pub accounts: Vec<Account>,
|
||||
pub mailbox_hashes: FnvHashMap<FolderHash, usize>,
|
||||
pub mailbox_hashes: FnvHashMap<MailboxHash, usize>,
|
||||
pub settings: Settings,
|
||||
|
||||
pub runtime_settings: Settings,
|
||||
|
@ -126,12 +126,12 @@ impl Context {
|
|||
pub fn account_status(
|
||||
&mut self,
|
||||
idx_a: usize,
|
||||
folder_hash: FolderHash,
|
||||
mailbox_hash: MailboxHash,
|
||||
) -> result::Result<(), usize> {
|
||||
match self.accounts[idx_a].status(folder_hash) {
|
||||
match self.accounts[idx_a].status(mailbox_hash) {
|
||||
Ok(()) => {
|
||||
self.replies
|
||||
.push_back(UIEvent::MailboxUpdate((idx_a, folder_hash)));
|
||||
.push_back(UIEvent::MailboxUpdate((idx_a, mailbox_hash)));
|
||||
Ok(())
|
||||
}
|
||||
Err(n) => Err(n),
|
||||
|
@ -149,13 +149,13 @@ impl Context {
|
|||
let ret = accounts[account_pos].is_online();
|
||||
if ret.is_ok() {
|
||||
if !was_online {
|
||||
for folder_node in accounts[account_pos].list_folders() {
|
||||
for mailbox_node in accounts[account_pos].list_mailboxes() {
|
||||
debug!(
|
||||
"hash & folder: {:?} {}",
|
||||
folder_node.hash,
|
||||
accounts[account_pos][&folder_node.hash].name()
|
||||
"hash & mailbox: {:?} {}",
|
||||
mailbox_node.hash,
|
||||
accounts[account_pos][&mailbox_node.hash].name()
|
||||
);
|
||||
mailbox_hashes.insert(folder_node.hash, account_pos);
|
||||
mailbox_hashes.insert(mailbox_node.hash, account_pos);
|
||||
}
|
||||
/* Account::watch() needs
|
||||
* - work_controller to pass `work_context` to the watcher threads and then add them
|
||||
|
@ -267,7 +267,7 @@ impl State {
|
|||
&backends,
|
||||
work_controller.get_context(),
|
||||
sender.clone(),
|
||||
NotifyFn::new(Box::new(move |f: FolderHash| {
|
||||
NotifyFn::new(Box::new(move |f: MailboxHash| {
|
||||
sender
|
||||
.send(ThreadEvent::UIEvent(UIEvent::WorkerProgress(f)))
|
||||
.unwrap();
|
||||
|
@ -347,7 +347,7 @@ impl State {
|
|||
for i in 0..s.context.accounts.len() {
|
||||
if s.context.is_online(i).is_ok() && s.context.accounts[i].is_empty() {
|
||||
return Err(MeliError::new(format!(
|
||||
"Account {} has no folders configured.",
|
||||
"Account {} has no mailboxes configured.",
|
||||
s.context.accounts[i].name()
|
||||
)));
|
||||
}
|
||||
|
@ -357,7 +357,7 @@ impl State {
|
|||
}
|
||||
|
||||
/*
|
||||
* When we receive a folder hash from a watcher thread,
|
||||
* When we receive a mailbox hash from a watcher thread,
|
||||
* we match the hash to the index of the mailbox, request a reload
|
||||
* and startup a thread to remind us to poll it every now and then till it's finished.
|
||||
*/
|
||||
|
@ -796,14 +796,14 @@ impl State {
|
|||
env::var(key.as_str()).unwrap_or_else(|e| e.to_string()),
|
||||
)));
|
||||
}
|
||||
Folder(account_name, op) => {
|
||||
Mailbox(account_name, op) => {
|
||||
if let Some(account) = self
|
||||
.context
|
||||
.accounts
|
||||
.iter_mut()
|
||||
.find(|a| a.name() == account_name)
|
||||
{
|
||||
match account.folder_operation(op) {
|
||||
match account.mailbox_operation(op) {
|
||||
Err(err) => {
|
||||
self.context.replies.push_back(UIEvent::StatusEvent(
|
||||
StatusEvent::DisplayMessage(err.to_string()),
|
||||
|
@ -911,9 +911,9 @@ impl State {
|
|||
self.child = Some(child);
|
||||
return;
|
||||
}
|
||||
UIEvent::WorkerProgress(folder_hash) => {
|
||||
if let Some(&account_idx) = self.context.mailbox_hashes.get(&folder_hash) {
|
||||
let _ = self.context.accounts[account_idx].status(folder_hash);
|
||||
UIEvent::WorkerProgress(mailbox_hash) => {
|
||||
if let Some(&account_idx) = self.context.mailbox_hashes.get(&mailbox_hash) {
|
||||
let _ = self.context.accounts[account_idx].status(mailbox_hash);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
14
src/types.rs
14
src/types.rs
|
@ -38,7 +38,7 @@ pub use self::helpers::*;
|
|||
use super::execute::Action;
|
||||
use super::terminal::*;
|
||||
|
||||
use melib::backends::FolderHash;
|
||||
use melib::backends::MailboxHash;
|
||||
use melib::{EnvelopeHash, RefreshEvent};
|
||||
use nix::unistd::Pid;
|
||||
use std;
|
||||
|
@ -63,7 +63,7 @@ pub enum ThreadEvent {
|
|||
Input(Key),
|
||||
/// User input and input as raw bytes.
|
||||
InputRaw((Key, Vec<u8>)),
|
||||
/// A watched folder has been refreshed.
|
||||
/// A watched Mailbox has been refreshed.
|
||||
RefreshMailbox(Box<RefreshEvent>),
|
||||
UIEvent(UIEvent),
|
||||
/// A thread has updated some of its information
|
||||
|
@ -110,13 +110,13 @@ pub enum UIEvent {
|
|||
Notification(Option<String>, String, Option<NotificationType>),
|
||||
Action(Action),
|
||||
StatusEvent(StatusEvent),
|
||||
MailboxUpdate((usize, FolderHash)), // (account_idx, mailbox_idx)
|
||||
MailboxDelete((usize, FolderHash)),
|
||||
MailboxCreate((usize, FolderHash)),
|
||||
MailboxUpdate((usize, MailboxHash)), // (account_idx, mailbox_idx)
|
||||
MailboxDelete((usize, MailboxHash)),
|
||||
MailboxCreate((usize, MailboxHash)),
|
||||
AccountStatusChange(usize),
|
||||
ComponentKill(Uuid),
|
||||
WorkerProgress(FolderHash),
|
||||
StartupCheck(FolderHash),
|
||||
WorkerProgress(MailboxHash),
|
||||
StartupCheck(MailboxHash),
|
||||
RefreshEvent(Box<RefreshEvent>),
|
||||
EnvelopeUpdate(EnvelopeHash),
|
||||
EnvelopeRename(EnvelopeHash, EnvelopeHash), // old_hash, new_hash
|
||||
|
|
Loading…
Reference in New Issue