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
|
.Ic toggle_menu_visibility Ns
|
||||||
).
|
).
|
||||||
.Pp
|
.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).
|
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
|
.Pp
|
||||||
If you're using a light color palette in your terminal, you may set
|
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
|
.Cm close
|
||||||
and select 'save as draft'.
|
and select 'save as draft'.
|
||||||
.Pp
|
.Pp
|
||||||
With no Draft or Sent folder,
|
With no Draft or Sent mailbox,
|
||||||
.Nm
|
.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
|
On complete failure to save your draft or sent message it will be saved in your
|
||||||
.Em tmp
|
.Em tmp
|
||||||
directory instead and you will be notified of its location.
|
directory instead and you will be notified of its location.
|
||||||
|
@ -307,17 +307,17 @@ filter mailbox with
|
||||||
key.
|
key.
|
||||||
Escape exits filter results
|
Escape exits filter results
|
||||||
.It Cm set read, set unread
|
.It Cm set read, set unread
|
||||||
.It Cm create-folder Ar ACCOUNT Ar FOLDER_PATH
|
.It Cm create-mailbox Ar ACCOUNT Ar MAILBOX_PATH
|
||||||
create folder with given path.
|
create mailbox with given path.
|
||||||
Be careful with backends and separator sensitivity (eg IMAP)
|
Be careful with backends and separator sensitivity (eg IMAP)
|
||||||
.It Cm subscribe-folder Ar ACCOUNT Ar FOLDER_PATH
|
.It Cm subscribe-mailbox Ar ACCOUNT Ar MAILBOX_PATH
|
||||||
subscribe to folder with given path
|
subscribe to mailbox with given path
|
||||||
.It Cm unsubscribe-folder Ar ACCOUNT Ar FOLDER_PATH
|
.It Cm unsubscribe-mailbox Ar ACCOUNT Ar MAILBOX_PATH
|
||||||
unsubscribe to folder with given path
|
unsubscribe to mailbox with given path
|
||||||
.It Cm rename-folder Ar ACCOUNT Ar FOLDER_PATH_SRC Ar FOLDER_PATH_DEST
|
.It Cm rename-mailbox Ar ACCOUNT Ar MAILBOX_PATH_SRC Ar MAILBOX_PATH_DEST
|
||||||
rename folder
|
rename mailbox
|
||||||
.It Cm delete-folder Ar ACCOUNT Ar FOLDER_PATH
|
.It Cm delete-mailbox Ar ACCOUNT Ar MAILBOX_PATH
|
||||||
delete folder
|
delete mailbox
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
envelope view commands:
|
envelope view commands:
|
||||||
|
@ -376,9 +376,9 @@ Non-complete list of shortcuts and their default values.
|
||||||
PageUp,
|
PageUp,
|
||||||
.It Ic next_page
|
.It Ic next_page
|
||||||
PageDown
|
PageDown
|
||||||
.It Ic prev_folder
|
.It Ic prev_mailbox
|
||||||
\&'K'
|
\&'K'
|
||||||
.It Ic next_folder
|
.It Ic next_mailbox
|
||||||
\&'J'
|
\&'J'
|
||||||
.It Ic prev_account
|
.It Ic prev_account
|
||||||
\&'l'
|
\&'l'
|
||||||
|
|
76
meli.conf.5
76
meli.conf.5
|
@ -56,23 +56,23 @@ example configuration
|
||||||
.Bd -literal
|
.Bd -literal
|
||||||
# Setting up a Maildir account
|
# Setting up a Maildir account
|
||||||
[accounts.account-name]
|
[accounts.account-name]
|
||||||
root_folder = "/path/to/root/folder"
|
root_mailbox = "/path/to/root/folder"
|
||||||
format = "Maildir"
|
format = "Maildir"
|
||||||
index_style = "Compact"
|
index_style = "Compact"
|
||||||
identity="email@address.tld"
|
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"
|
display_name = "Name"
|
||||||
|
|
||||||
# Set folder-specific settings
|
# Set mailbox-specific settings
|
||||||
[accounts.account-name.folders]
|
[accounts.account-name.mailboxes]
|
||||||
"INBOX" = { alias="Inbox" } #inline table
|
"INBOX" = { alias="Inbox" } #inline table
|
||||||
"drafts" = { alias="Drafts" } #inline table
|
"drafts" = { alias="Drafts" } #inline table
|
||||||
[accounts.account-name.folders."foobar-devel"] # or a regular table
|
[accounts.account-name.mailboxes."foobar-devel"] # or a regular table
|
||||||
ignore = true # don't show notifications for this folder
|
ignore = true # don't show notifications for this mailbox
|
||||||
|
|
||||||
# Setting up an mbox account
|
# Setting up an mbox account
|
||||||
[accounts.mbox]
|
[accounts.mbox]
|
||||||
root_folder = "/var/mail/username"
|
root_mailbox = "/var/mail/username"
|
||||||
format = "mbox"
|
format = "mbox"
|
||||||
index_style = "Compact"
|
index_style = "Compact"
|
||||||
identity="username@hostname.local"
|
identity="username@hostname.local"
|
||||||
|
@ -105,16 +105,16 @@ available options are listed below.
|
||||||
.Sy default values are shown in parentheses.
|
.Sy default values are shown in parentheses.
|
||||||
.Sh ACCOUNTS
|
.Sh ACCOUNTS
|
||||||
.Bl -tag -width 36n
|
.Bl -tag -width 36n
|
||||||
.It Ic root_folder Ar String
|
.It Ic root_mailbox Ar String
|
||||||
the backend-specific path of the root_folder, usually INBOX.
|
the backend-specific path of the root_mailbox, usually INBOX.
|
||||||
.It Ic format Ar String Op maildir mbox imap notmuch jmap
|
.It Ic format Ar String Op maildir mbox imap notmuch jmap
|
||||||
the format of the mail backend.
|
the format of the mail backend.
|
||||||
.It Ic subscribed_folders Ar [String,]
|
.It Ic subscribed_mailboxes Ar [String,]
|
||||||
an array of folder paths to display in the UI.
|
an array of mailbox paths to display in the UI.
|
||||||
Paths are relative to the root folder (eg "INBOX/Sent", not "Sent").
|
Paths are relative to the root mailbox (eg "INBOX/Sent", not "Sent").
|
||||||
The glob wildcard
|
The glob wildcard
|
||||||
.Em \&*
|
.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
|
.It Ic identity Ar String
|
||||||
your e-mail address that is inserted in the From: headers of outgoing mail
|
your e-mail address that is inserted in the From: headers of outgoing mail
|
||||||
.It Ic index_style Ar String
|
.It Ic index_style Ar String
|
||||||
|
@ -148,20 +148,20 @@ Available options are 'none' and 'sqlite3'
|
||||||
.It Ic vcard_folder Ar String
|
.It Ic vcard_folder Ar String
|
||||||
(optional) Folder that contains .vcf files.
|
(optional) Folder that contains .vcf files.
|
||||||
They are parsed and imported read-only.
|
They are parsed and imported read-only.
|
||||||
.It Ic folders Ar folder_config
|
.It Ic mailboxes Ar mailbox
|
||||||
(optional) configuration for each folder.
|
(optional) configuration for each mailbox.
|
||||||
Its format is described below in
|
Its format is described below in
|
||||||
.Sx FOLDERS Ns
|
.Sx mailboxes Ns
|
||||||
\&.
|
\&.
|
||||||
.El
|
.El
|
||||||
.Sh notmuch only
|
.Sh notmuch only
|
||||||
.Ic root_folder
|
.Ic root_mailbox
|
||||||
points to the directory which contains the
|
points to the directory which contains the
|
||||||
.Pa .notmuch/
|
.Pa .notmuch/
|
||||||
subdirectory.
|
subdirectory.
|
||||||
notmuch folders are virtual, since they are defined by user-given notmuch queries.
|
notmuch mailboxes are virtual, since they are defined by user-given notmuch queries.
|
||||||
Thus you have to explicitly state the folders you want in the
|
Thus you have to explicitly state the mailboxes you want in the
|
||||||
.Ic folders
|
.Ic mailboxes
|
||||||
field and set the
|
field and set the
|
||||||
.Ar query
|
.Ar query
|
||||||
property to each of them.
|
property to each of them.
|
||||||
|
@ -170,7 +170,7 @@ Example:
|
||||||
[accounts.notmuch]
|
[accounts.notmuch]
|
||||||
format = "notmuch"
|
format = "notmuch"
|
||||||
\&...
|
\&...
|
||||||
[accounts.notmuch.folders]
|
[accounts.notmuch.mailboxes]
|
||||||
"INBOX" = { query="tag:inbox", subscribe = true }
|
"INBOX" = { query="tag:inbox", subscribe = true }
|
||||||
"Drafts" = { query="tag:draft", subscribe = true }
|
"Drafts" = { query="tag:draft", subscribe = true }
|
||||||
"Sent" = { query="from:username@server.tld from:username2@server.tld", subscribe = true }
|
"Sent" = { query="from:username@server.tld from:username2@server.tld", subscribe = true }
|
||||||
|
@ -213,22 +213,22 @@ example:
|
||||||
.\" default value
|
.\" default value
|
||||||
.Pq Em false
|
.Pq Em false
|
||||||
.El
|
.El
|
||||||
.Sh FOLDERS
|
.Sh mailboxes
|
||||||
.Bl -tag -width 36n
|
.Bl -tag -width 36n
|
||||||
.It Ic alias Ar String
|
.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
|
.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
|
.It Ic subscribe Ar boolean
|
||||||
(optional) watch this folder for updates
|
(optional) watch this mailbox for updates
|
||||||
.\" default value
|
.\" default value
|
||||||
.Pq Em true
|
.Pq Em true
|
||||||
.It Ic ignore Ar boolean
|
.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
|
.\" default value
|
||||||
.Pq Em false
|
.Pq Em false
|
||||||
.It Ic usage Ar boolean
|
.It Ic usage Ar boolean
|
||||||
(optional) special usage of this folder.
|
(optional) special usage of this mailbox.
|
||||||
Valid values are:
|
Valid values are:
|
||||||
.Bl -bullet -compact
|
.Bl -bullet -compact
|
||||||
.It
|
.It
|
||||||
|
@ -248,9 +248,9 @@ Valid values are:
|
||||||
.It
|
.It
|
||||||
.Ar Trash
|
.Ar Trash
|
||||||
.El
|
.El
|
||||||
otherwise usage is inferred from the folder title.
|
otherwise usage is inferred from the mailbox title.
|
||||||
.It Ic conf_override Ar boolean
|
.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
|
Available sections to override are
|
||||||
.Em pager, notifications, shortcuts, composing
|
.Em pager, notifications, shortcuts, composing
|
||||||
and the account options
|
and the account options
|
||||||
|
@ -258,9 +258,9 @@ and the account options
|
||||||
\&.
|
\&.
|
||||||
Example:
|
Example:
|
||||||
.Bd -literal
|
.Bd -literal
|
||||||
[accounts."imap.domain.tld".folders."INBOX"]
|
[accounts."imap.domain.tld".mailboxes."INBOX"]
|
||||||
index_style = "plain"
|
index_style = "plain"
|
||||||
[accounts."imap.domain.tld".folders."INBOX".pager]
|
[accounts."imap.domain.tld".mailboxes."INBOX".pager]
|
||||||
filter = ""
|
filter = ""
|
||||||
.Ed
|
.Ed
|
||||||
.El
|
.El
|
||||||
|
@ -347,12 +347,12 @@ Go to previous page.
|
||||||
Go to next page.
|
Go to next page.
|
||||||
.\" default value
|
.\" default value
|
||||||
.Pq Em PageDown
|
.Pq Em PageDown
|
||||||
.It Ic prev_folder
|
.It Ic prev_mailbox
|
||||||
Go to previous folder.
|
Go to previous mailbox.
|
||||||
.\" default value
|
.\" default value
|
||||||
.Pq Em K
|
.Pq Em K
|
||||||
.It Ic next_folder
|
.It Ic next_mailbox
|
||||||
Go to next folder.
|
Go to next mailbox.
|
||||||
.\" default value
|
.\" default value
|
||||||
.Pq Em J
|
.Pq Em J
|
||||||
.It Ic prev_account
|
.It Ic prev_account
|
||||||
|
@ -372,7 +372,7 @@ Set thread as seen.
|
||||||
.\" default value
|
.\" default value
|
||||||
.Pq Em n
|
.Pq Em n
|
||||||
.It Ic refresh
|
.It Ic refresh
|
||||||
Manually request a folder refresh.
|
Manually request a mailbox refresh.
|
||||||
.\" default value
|
.\" default value
|
||||||
.Pq Em F5
|
.Pq Em F5
|
||||||
.It Ic search
|
.It Ic search
|
||||||
|
@ -622,8 +622,8 @@ example configuration:
|
||||||
colors = { signed="#Ff6600", replied="DeepSkyBlue4", draft="#f00", replied="8" }
|
colors = { signed="#Ff6600", replied="DeepSkyBlue4", draft="#f00", replied="8" }
|
||||||
[accounts.dummy]
|
[accounts.dummy]
|
||||||
\&...
|
\&...
|
||||||
[accounts.dummy.folders]
|
[accounts.dummy.mailboxes]
|
||||||
# per folder override:
|
# per mailbox override:
|
||||||
"INBOX" = { tags.ignore_tags=["inbox", ] }
|
"INBOX" = { tags.ignore_tags=["inbox", ] }
|
||||||
.Ed
|
.Ed
|
||||||
.Sh PGP
|
.Sh PGP
|
||||||
|
|
|
@ -193,12 +193,12 @@ pub enum RefreshEventKind {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct RefreshEvent {
|
pub struct RefreshEvent {
|
||||||
hash: FolderHash,
|
hash: MailboxHash,
|
||||||
kind: RefreshEventKind,
|
kind: RefreshEventKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RefreshEvent {
|
impl RefreshEvent {
|
||||||
pub fn hash(&self) -> FolderHash {
|
pub fn hash(&self) -> MailboxHash {
|
||||||
self.hash
|
self.hash
|
||||||
}
|
}
|
||||||
pub fn kind(self) -> RefreshEventKind {
|
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 {
|
impl fmt::Debug for NotifyFn {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
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 {
|
impl From<Box<dyn Fn(MailboxHash) -> () + Send + Sync>> for NotifyFn {
|
||||||
fn from(kind: Box<dyn Fn(FolderHash) -> () + Send + Sync>) -> Self {
|
fn from(kind: Box<dyn Fn(MailboxHash) -> () + Send + Sync>) -> Self {
|
||||||
NotifyFn(kind)
|
NotifyFn(kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NotifyFn {
|
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)
|
NotifyFn(b)
|
||||||
}
|
}
|
||||||
pub fn notify(&self, f: FolderHash) {
|
pub fn notify(&self, f: MailboxHash) {
|
||||||
self.0(f);
|
self.0(f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -246,10 +246,10 @@ impl NotifyFn {
|
||||||
pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
|
pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
|
||||||
fn is_online(&self) -> Result<()>;
|
fn is_online(&self) -> Result<()>;
|
||||||
fn connect(&mut self) {}
|
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(
|
fn refresh(
|
||||||
&mut self,
|
&mut self,
|
||||||
_folder_hash: FolderHash,
|
_mailbox_hash: MailboxHash,
|
||||||
_sender: RefreshEventConsumer,
|
_sender: RefreshEventConsumer,
|
||||||
) -> Result<Async<()>> {
|
) -> Result<Async<()>> {
|
||||||
Err(MeliError::new("Unimplemented."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
|
@ -259,10 +259,10 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
|
||||||
sender: RefreshEventConsumer,
|
sender: RefreshEventConsumer,
|
||||||
work_context: WorkContext,
|
work_context: WorkContext,
|
||||||
) -> Result<std::thread::ThreadId>;
|
) -> 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 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>>>> {
|
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -272,32 +272,32 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_folder(
|
fn create_mailbox(
|
||||||
&mut self,
|
&mut self,
|
||||||
_path: String,
|
_path: String,
|
||||||
) -> Result<(FolderHash, FnvHashMap<FolderHash, Folder>)> {
|
) -> Result<(MailboxHash, FnvHashMap<MailboxHash, Mailbox>)> {
|
||||||
Err(MeliError::new("Unimplemented."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_folder(
|
fn delete_mailbox(
|
||||||
&mut self,
|
&mut self,
|
||||||
_folder_hash: FolderHash,
|
_mailbox_hash: MailboxHash,
|
||||||
) -> Result<FnvHashMap<FolderHash, Folder>> {
|
) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||||
Err(MeliError::new("Unimplemented."))
|
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."))
|
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."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_folder_permissions(
|
fn set_mailbox_permissions(
|
||||||
&mut self,
|
&mut self,
|
||||||
_folder_hash: FolderHash,
|
_mailbox_hash: MailboxHash,
|
||||||
_val: FolderPermissions,
|
_val: MailboxPermissions,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
Err(MeliError::new("Unimplemented."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
}
|
}
|
||||||
|
@ -315,7 +315,7 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync {
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
/// /* Create operation from Backend */
|
/// /* Create operation from Backend */
|
||||||
///
|
///
|
||||||
/// let op = backend.operation(message.hash(), mailbox.folder.hash());
|
/// let op = backend.operation(message.hash(), mailbox.hash());
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
@ -443,30 +443,30 @@ impl SpecialUsageMailbox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait BackendFolder: Debug {
|
pub trait BackendMailbox: Debug {
|
||||||
fn hash(&self) -> FolderHash;
|
fn hash(&self) -> MailboxHash;
|
||||||
fn name(&self) -> &str;
|
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 path(&self) -> &str;
|
||||||
fn change_name(&mut self, new_name: &str);
|
fn change_name(&mut self, new_name: &str);
|
||||||
fn clone(&self) -> Folder;
|
fn clone(&self) -> Mailbox;
|
||||||
fn children(&self) -> &[FolderHash];
|
fn children(&self) -> &[MailboxHash];
|
||||||
fn parent(&self) -> Option<FolderHash>;
|
fn parent(&self) -> Option<MailboxHash>;
|
||||||
fn is_subscribed(&self) -> bool;
|
fn is_subscribed(&self) -> bool;
|
||||||
fn set_is_subscribed(&mut self, new_val: bool) -> Result<()>;
|
fn set_is_subscribed(&mut self, new_val: bool) -> Result<()>;
|
||||||
fn set_special_usage(&mut self, new_val: SpecialUsageMailbox) -> Result<()>;
|
fn set_special_usage(&mut self, new_val: SpecialUsageMailbox) -> Result<()>;
|
||||||
fn special_usage(&self) -> SpecialUsageMailbox;
|
fn special_usage(&self) -> SpecialUsageMailbox;
|
||||||
fn permissions(&self) -> FolderPermissions;
|
fn permissions(&self) -> MailboxPermissions;
|
||||||
fn count(&self) -> Result<(usize, usize)>;
|
fn count(&self) -> Result<(usize, usize)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct DummyFolder {
|
struct DummyMailbox {
|
||||||
v: Vec<FolderHash>,
|
v: Vec<MailboxHash>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackendFolder for DummyFolder {
|
impl BackendMailbox for DummyMailbox {
|
||||||
fn hash(&self) -> FolderHash {
|
fn hash(&self) -> MailboxHash {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,24 +480,24 @@ impl BackendFolder for DummyFolder {
|
||||||
|
|
||||||
fn change_name(&mut self, _s: &str) {}
|
fn change_name(&mut self, _s: &str) {}
|
||||||
|
|
||||||
fn clone(&self) -> Folder {
|
fn clone(&self) -> Mailbox {
|
||||||
folder_default()
|
mailbox_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn special_usage(&self) -> SpecialUsageMailbox {
|
fn special_usage(&self) -> SpecialUsageMailbox {
|
||||||
SpecialUsageMailbox::Normal
|
SpecialUsageMailbox::Normal
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children(&self) -> &[FolderHash] {
|
fn children(&self) -> &[MailboxHash] {
|
||||||
&self.v
|
&self.v
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parent(&self) -> Option<FolderHash> {
|
fn parent(&self) -> Option<MailboxHash> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn permissions(&self) -> FolderPermissions {
|
fn permissions(&self) -> MailboxPermissions {
|
||||||
FolderPermissions::default()
|
MailboxPermissions::default()
|
||||||
}
|
}
|
||||||
fn is_subscribed(&self) -> bool {
|
fn is_subscribed(&self) -> bool {
|
||||||
true
|
true
|
||||||
|
@ -513,29 +513,29 @@ impl BackendFolder for DummyFolder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn folder_default() -> Folder {
|
pub fn mailbox_default() -> Mailbox {
|
||||||
Box::new(DummyFolder {
|
Box::new(DummyMailbox {
|
||||||
v: Vec::with_capacity(0),
|
v: Vec::with_capacity(0),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type FolderHash = u64;
|
pub type MailboxHash = u64;
|
||||||
pub type Folder = Box<dyn BackendFolder + Send + Sync>;
|
pub type Mailbox = Box<dyn BackendMailbox + Send + Sync>;
|
||||||
|
|
||||||
impl Clone for Folder {
|
impl Clone for Mailbox {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
BackendFolder::clone(self.deref())
|
BackendMailbox::clone(self.deref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Folder {
|
impl Default for Mailbox {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
folder_default()
|
mailbox_default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
|
||||||
pub struct FolderPermissions {
|
pub struct MailboxPermissions {
|
||||||
pub create_messages: bool,
|
pub create_messages: bool,
|
||||||
pub remove_messages: bool,
|
pub remove_messages: bool,
|
||||||
pub set_flags: bool,
|
pub set_flags: bool,
|
||||||
|
@ -546,22 +546,22 @@ pub struct FolderPermissions {
|
||||||
pub change_permissions: bool,
|
pub change_permissions: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FolderPermissions {
|
impl Default for MailboxPermissions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
FolderPermissions {
|
MailboxPermissions {
|
||||||
create_messages: false,
|
create_messages: false,
|
||||||
remove_messages: false,
|
remove_messages: false,
|
||||||
set_flags: false,
|
set_flags: false,
|
||||||
create_child: false,
|
create_child: false,
|
||||||
rename_messages: false,
|
rename_messages: false,
|
||||||
delete_messages: false,
|
delete_messages: false,
|
||||||
delete_mailbox: false,
|
delete_mailbox: true,
|
||||||
change_permissions: false,
|
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 {
|
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
write!(fmt, "{:#?}", self)
|
write!(fmt, "{:#?}", self)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,8 @@ use smallvec::SmallVec;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod protocol_parser;
|
mod protocol_parser;
|
||||||
pub use protocol_parser::{UntaggedResponse::*, *};
|
pub use protocol_parser::{UntaggedResponse::*, *};
|
||||||
mod folder;
|
mod mailbox;
|
||||||
pub use folder::*;
|
pub use mailbox::*;
|
||||||
mod operations;
|
mod operations;
|
||||||
pub use operations::*;
|
pub use operations::*;
|
||||||
mod connection;
|
mod connection;
|
||||||
|
@ -35,10 +35,10 @@ pub use watch::*;
|
||||||
|
|
||||||
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
||||||
use crate::backends::BackendOp;
|
use crate::backends::BackendOp;
|
||||||
use crate::backends::FolderHash;
|
use crate::backends::MailboxHash;
|
||||||
use crate::backends::RefreshEvent;
|
use crate::backends::RefreshEvent;
|
||||||
use crate::backends::RefreshEventKind::{self, *};
|
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::conf::AccountSettings;
|
||||||
use crate::email::*;
|
use crate::email::*;
|
||||||
use crate::error::{MeliError, Result};
|
use crate::error::{MeliError, Result};
|
||||||
|
@ -117,8 +117,8 @@ macro_rules! get_conf_val {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct UIDStore {
|
pub struct UIDStore {
|
||||||
uidvalidity: Arc<Mutex<FnvHashMap<FolderHash, UID>>>,
|
uidvalidity: Arc<Mutex<FnvHashMap<MailboxHash, UID>>>,
|
||||||
hash_index: Arc<Mutex<FnvHashMap<EnvelopeHash, (UID, FolderHash)>>>,
|
hash_index: Arc<Mutex<FnvHashMap<EnvelopeHash, (UID, MailboxHash)>>>,
|
||||||
uid_index: Arc<Mutex<FnvHashMap<UID, EnvelopeHash>>>,
|
uid_index: Arc<Mutex<FnvHashMap<UID, EnvelopeHash>>>,
|
||||||
|
|
||||||
byte_cache: Arc<Mutex<FnvHashMap<UID, EnvelopeCache>>>,
|
byte_cache: Arc<Mutex<FnvHashMap<UID, EnvelopeCache>>>,
|
||||||
|
@ -134,7 +134,7 @@ pub struct ImapType {
|
||||||
can_create_flags: Arc<Mutex<bool>>,
|
can_create_flags: Arc<Mutex<bool>>,
|
||||||
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||||
|
|
||||||
folders: Arc<RwLock<FnvHashMap<FolderHash, ImapFolder>>>,
|
mailboxes: Arc<RwLock<FnvHashMap<MailboxHash, ImapMailbox>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MailBackend for ImapType {
|
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 mut w = AsyncBuilder::new();
|
||||||
let handle = {
|
let handle = {
|
||||||
let tx = w.tx();
|
let tx = w.tx();
|
||||||
let uid_store = self.uid_store.clone();
|
let uid_store = self.uid_store.clone();
|
||||||
let tag_index = self.tag_index.clone();
|
let tag_index = self.tag_index.clone();
|
||||||
let can_create_flags = self.can_create_flags.clone();
|
let can_create_flags = self.can_create_flags.clone();
|
||||||
let folder_hash = folder.hash();
|
let mailbox_hash = mailbox.hash();
|
||||||
let (permissions, folder_path, folder_exists, no_select, unseen) = {
|
let (permissions, mailbox_path, mailbox_exists, no_select, unseen) = {
|
||||||
let f = &self.folders.read().unwrap()[&folder_hash];
|
let f = &self.mailboxes.read().unwrap()[&mailbox_hash];
|
||||||
(
|
(
|
||||||
f.permissions.clone(),
|
f.permissions.clone(),
|
||||||
f.imap_path().to_string(),
|
f.imap_path().to_string(),
|
||||||
|
@ -182,24 +182,24 @@ impl MailBackend for ImapType {
|
||||||
let tx = _tx;
|
let tx = _tx;
|
||||||
let mut response = String::with_capacity(8 * 1024);
|
let mut response = String::with_capacity(8 * 1024);
|
||||||
let mut conn = connection.lock()?;
|
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
|
/* first SELECT the mailbox to get READ/WRITE permissions (because EXAMINE only
|
||||||
* returns READ-ONLY for both cases) */
|
* 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)?;
|
conn.read_response(&mut response)?;
|
||||||
let examine_response = protocol_parser::select_response(&response)?;
|
let examine_response = protocol_parser::select_response(&response)?;
|
||||||
*can_create_flags.lock().unwrap() = examine_response.can_create_flags;
|
*can_create_flags.lock().unwrap() = examine_response.can_create_flags;
|
||||||
debug!(
|
debug!(
|
||||||
"folder: {} examine_response: {:?}",
|
"mailbox: {} examine_response: {:?}",
|
||||||
folder_path, examine_response
|
mailbox_path, examine_response
|
||||||
);
|
);
|
||||||
let mut exists: usize = examine_response.uidnext - 1;
|
let mut exists: usize = examine_response.uidnext - 1;
|
||||||
{
|
{
|
||||||
let mut uidvalidities = uid_store.uidvalidity.lock().unwrap();
|
let mut uidvalidities = uid_store.uidvalidity.lock().unwrap();
|
||||||
|
|
||||||
let v = uidvalidities
|
let v = uidvalidities
|
||||||
.entry(folder_hash)
|
.entry(mailbox_hash)
|
||||||
.or_insert(examine_response.uidvalidity);
|
.or_insert(examine_response.uidvalidity);
|
||||||
*v = examine_response.uidvalidity;
|
*v = examine_response.uidvalidity;
|
||||||
|
|
||||||
|
@ -210,11 +210,11 @@ impl MailBackend for ImapType {
|
||||||
permissions.rename_messages = !examine_response.read_only;
|
permissions.rename_messages = !examine_response.read_only;
|
||||||
permissions.delete_messages = !examine_response.read_only;
|
permissions.delete_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();
|
let mut mailbox_exists = mailbox_exists.lock().unwrap();
|
||||||
*folder_exists = exists;
|
*mailbox_exists = exists;
|
||||||
}
|
}
|
||||||
/* reselecting the same mailbox with EXAMINE prevents expunging it */
|
/* 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)?;
|
conn.read_response(&mut response)?;
|
||||||
|
|
||||||
let mut tag_lck = tag_index.write().unwrap();
|
let mut tag_lck = tag_index.write().unwrap();
|
||||||
|
@ -247,7 +247,7 @@ impl MailBackend for ImapType {
|
||||||
let mut env = envelope.unwrap();
|
let mut env = envelope.unwrap();
|
||||||
let mut h = DefaultHasher::new();
|
let mut h = DefaultHasher::new();
|
||||||
h.write_usize(uid);
|
h.write_usize(uid);
|
||||||
h.write(folder_path.as_bytes());
|
h.write(mailbox_path.as_bytes());
|
||||||
env.set_hash(h.finish());
|
env.set_hash(h.finish());
|
||||||
if let Some((flags, keywords)) = flags {
|
if let Some((flags, keywords)) = flags {
|
||||||
if !flags.contains(Flag::SEEN) {
|
if !flags.contains(Flag::SEEN) {
|
||||||
|
@ -266,7 +266,7 @@ impl MailBackend for ImapType {
|
||||||
.hash_index
|
.hash_index
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(env.hash(), (uid, folder_hash));
|
.insert(env.hash(), (uid, mailbox_hash));
|
||||||
uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
|
uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
|
||||||
envelopes.push(env);
|
envelopes.push(env);
|
||||||
}
|
}
|
||||||
|
@ -290,15 +290,15 @@ impl MailBackend for ImapType {
|
||||||
|
|
||||||
fn refresh(
|
fn refresh(
|
||||||
&mut self,
|
&mut self,
|
||||||
folder_hash: FolderHash,
|
mailbox_hash: MailboxHash,
|
||||||
sender: RefreshEventConsumer,
|
sender: RefreshEventConsumer,
|
||||||
) -> Result<Async<()>> {
|
) -> Result<Async<()>> {
|
||||||
self.connection.lock().unwrap().connect()?;
|
self.connection.lock().unwrap().connect()?;
|
||||||
let inbox = self
|
let inbox = self
|
||||||
.folders
|
.mailboxes
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get(&folder_hash)
|
.get(&mailbox_hash)
|
||||||
.map(std::clone::Clone::clone)
|
.map(std::clone::Clone::clone)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let tag_index = self.tag_index.clone();
|
let tag_index = self.tag_index.clone();
|
||||||
|
@ -340,7 +340,7 @@ impl MailBackend for ImapType {
|
||||||
sender: RefreshEventConsumer,
|
sender: RefreshEventConsumer,
|
||||||
work_context: WorkContext,
|
work_context: WorkContext,
|
||||||
) -> Result<std::thread::ThreadId> {
|
) -> Result<std::thread::ThreadId> {
|
||||||
let folders = self.folders.clone();
|
let mailboxes = self.mailboxes.clone();
|
||||||
let tag_index = self.tag_index.clone();
|
let tag_index = self.tag_index.clone();
|
||||||
let conn = ImapConnection::new_connection(&self.server_conf, self.online.clone());
|
let conn = ImapConnection::new_connection(&self.server_conf, self.online.clone());
|
||||||
let main_conn = self.connection.clone();
|
let main_conn = self.connection.clone();
|
||||||
|
@ -365,7 +365,7 @@ impl MailBackend for ImapType {
|
||||||
is_online,
|
is_online,
|
||||||
main_conn,
|
main_conn,
|
||||||
uid_store,
|
uid_store,
|
||||||
folders,
|
mailboxes,
|
||||||
sender,
|
sender,
|
||||||
work_context,
|
work_context,
|
||||||
tag_index,
|
tag_index,
|
||||||
|
@ -379,38 +379,41 @@ impl MailBackend for ImapType {
|
||||||
Ok(handle.thread().id())
|
Ok(handle.thread().id())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
|
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||||
{
|
{
|
||||||
let folders = self.folders.read().unwrap();
|
let mailboxes = self.mailboxes.read().unwrap();
|
||||||
if !folders.is_empty() {
|
if !mailboxes.is_empty() {
|
||||||
return Ok(folders
|
return Ok(mailboxes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(h, f)| (*h, Box::new(Clone::clone(f)) as Folder))
|
.map(|(h, f)| (*h, Box::new(Clone::clone(f)) as Mailbox))
|
||||||
.collect());
|
.collect());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut folders = self.folders.write()?;
|
let mut mailboxes = self.mailboxes.write()?;
|
||||||
*folders = ImapType::imap_folders(&self.connection)?;
|
*mailboxes = ImapType::imap_mailboxes(&self.connection)?;
|
||||||
folders.retain(|_, f| (self.is_subscribed)(f.path()));
|
mailboxes.retain(|_, f| (self.is_subscribed)(f.path()));
|
||||||
let keys = folders.keys().cloned().collect::<FnvHashSet<FolderHash>>();
|
let keys = mailboxes
|
||||||
|
.keys()
|
||||||
|
.cloned()
|
||||||
|
.collect::<FnvHashSet<MailboxHash>>();
|
||||||
let mut uid_lock = self.uid_store.uidvalidity.lock().unwrap();
|
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();
|
uid_lock.entry(f.hash()).or_default();
|
||||||
f.children.retain(|c| keys.contains(c));
|
f.children.retain(|c| keys.contains(c));
|
||||||
}
|
}
|
||||||
drop(uid_lock);
|
drop(uid_lock);
|
||||||
Ok(folders
|
Ok(mailboxes
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, f)| f.is_subscribed)
|
.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())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp> {
|
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(
|
Box::new(ImapOp::new(
|
||||||
uid,
|
uid,
|
||||||
self.folders.read().unwrap()[&folder_hash]
|
self.mailboxes.read().unwrap()[&mailbox_hash]
|
||||||
.imap_path()
|
.imap_path()
|
||||||
.to_string(),
|
.to_string(),
|
||||||
self.connection.clone(),
|
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 path = {
|
||||||
let folders = self.folders.read().unwrap();
|
let mailboxes = self.mailboxes.read().unwrap();
|
||||||
|
|
||||||
let f_result = folders
|
let f_result = mailboxes
|
||||||
.values()
|
.values()
|
||||||
.find(|v| v.path == folder || v.name == folder);
|
.find(|v| v.path == mailbox || v.name == mailbox);
|
||||||
if f_result
|
if f_result
|
||||||
.map(|f| !f.permissions.lock().unwrap().create_messages)
|
.map(|f| !f.permissions.lock().unwrap().create_messages)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"You are not allowed to create messages in folder {}",
|
"You are not allowed to create messages in mailbox {}",
|
||||||
folder
|
mailbox
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
f_result
|
f_result
|
||||||
.map(|v| v.imap_path().to_string())
|
.map(|v| v.imap_path().to_string())
|
||||||
.ok_or(MeliError::new(format!(
|
.ok_or(MeliError::new(format!(
|
||||||
"Folder with name {} not found.",
|
"Mailbox with name {} not found.",
|
||||||
folder
|
mailbox
|
||||||
)))?
|
)))?
|
||||||
};
|
};
|
||||||
let mut response = String::with_capacity(8 * 1024);
|
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 self,
|
||||||
mut path: String,
|
mut path: String,
|
||||||
) -> Result<(FolderHash, FnvHashMap<FolderHash, Folder>)> {
|
) -> Result<(MailboxHash, FnvHashMap<MailboxHash, Mailbox>)> {
|
||||||
/* Must transform path to something the IMAP server will accept
|
/* Must transform path to something the IMAP server will accept
|
||||||
*
|
*
|
||||||
* Each root mailbox has a hierarchy delimeter reported by the LIST entry. All paths
|
* 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.
|
* decision is unpleasant for you.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let mut folders = self.folders.write().unwrap();
|
let mut mailboxes = self.mailboxes.write().unwrap();
|
||||||
for root_folder in folders.values().filter(|f| f.parent.is_none()) {
|
for root_mailbox in mailboxes.values().filter(|f| f.parent.is_none()) {
|
||||||
if path.starts_with(&root_folder.name) {
|
if path.starts_with(&root_mailbox.name) {
|
||||||
debug!("path starts with {:?}", &root_folder);
|
debug!("path starts with {:?}", &root_mailbox);
|
||||||
path = path.replace(
|
path = path.replace(
|
||||||
'/',
|
'/',
|
||||||
(root_folder.separator as char).encode_utf8(&mut [0; 4]),
|
(root_mailbox.separator as char).encode_utf8(&mut [0; 4]),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if folders.values().any(|f| f.path == path) {
|
if mailboxes.values().any(|f| f.path == path) {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"Folder named `{}` in account `{}` already exists.",
|
"Mailbox named `{}` in account `{}` already exists.",
|
||||||
path, self.account_name,
|
path, self.account_name,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -527,21 +530,24 @@ impl MailBackend for ImapType {
|
||||||
let ret: Result<()> = ImapResponse::from(&response).into();
|
let ret: Result<()> = ImapResponse::from(&response).into();
|
||||||
ret?;
|
ret?;
|
||||||
let new_hash = get_path_hash!(path.as_str());
|
let new_hash = get_path_hash!(path.as_str());
|
||||||
folders.clear();
|
mailboxes.clear();
|
||||||
drop(folders);
|
drop(mailboxes);
|
||||||
Ok((new_hash, self.folders().map_err(|err| MeliError::new(format!("Mailbox create was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err)))?))
|
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>> {
|
fn delete_mailbox(
|
||||||
let mut folders = self.folders.write().unwrap();
|
&mut self,
|
||||||
let permissions = folders[&folder_hash].permissions();
|
mailbox_hash: MailboxHash,
|
||||||
|
) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||||
|
let mut mailboxes = self.mailboxes.write().unwrap();
|
||||||
|
let permissions = mailboxes[&mailbox_hash].permissions();
|
||||||
if !permissions.delete_mailbox {
|
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 response = String::with_capacity(8 * 1024);
|
||||||
{
|
{
|
||||||
let mut conn_lck = self.connection.lock()?;
|
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
|
/* make sure mailbox is not selected before it gets deleted, otherwise
|
||||||
* connection gets dropped by server */
|
* connection gets dropped by server */
|
||||||
if conn_lck
|
if conn_lck
|
||||||
|
@ -550,42 +556,46 @@ impl MailBackend for ImapType {
|
||||||
.any(|cap| cap.eq_ignore_ascii_case(b"UNSELECT"))
|
.any(|cap| cap.eq_ignore_ascii_case(b"UNSELECT"))
|
||||||
{
|
{
|
||||||
conn_lck.send_command(
|
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)?;
|
conn_lck.read_response(&mut response)?;
|
||||||
} else {
|
} else {
|
||||||
conn_lck.send_command(
|
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.read_response(&mut response)?;
|
||||||
conn_lck.send_command(
|
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)?;
|
conn_lck.read_response(&mut response)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if folders[&folder_hash].is_subscribed() {
|
if mailboxes[&mailbox_hash].is_subscribed() {
|
||||||
conn_lck.send_command(
|
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.read_response(&mut response)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
conn_lck.send_command(
|
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)?;
|
conn_lck.read_response(&mut response)?;
|
||||||
}
|
}
|
||||||
let ret: Result<()> = ImapResponse::from(&response).into();
|
let ret: Result<()> = ImapResponse::from(&response).into();
|
||||||
ret?;
|
ret?;
|
||||||
folders.clear();
|
mailboxes.clear();
|
||||||
drop(folders);
|
drop(mailboxes);
|
||||||
self.folders().map_err(|err| format!("Mailbox delete was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err).into())
|
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<()> {
|
fn set_mailbox_subscription(&mut self, mailbox_hash: MailboxHash, new_val: bool) -> Result<()> {
|
||||||
let mut folders = self.folders.write().unwrap();
|
let mut mailboxes = self.mailboxes.write().unwrap();
|
||||||
if folders[&folder_hash].is_subscribed() == new_val {
|
if mailboxes[&mailbox_hash].is_subscribed() == new_val {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -594,11 +604,11 @@ impl MailBackend for ImapType {
|
||||||
let mut conn_lck = self.connection.lock()?;
|
let mut conn_lck = self.connection.lock()?;
|
||||||
if new_val {
|
if new_val {
|
||||||
conn_lck.send_command(
|
conn_lck.send_command(
|
||||||
format!("SUBSCRIBE \"{}\"", folders[&folder_hash].imap_path()).as_bytes(),
|
format!("SUBSCRIBE \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} else {
|
||||||
conn_lck.send_command(
|
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.read_response(&mut response)?;
|
||||||
|
@ -606,24 +616,28 @@ impl MailBackend for ImapType {
|
||||||
|
|
||||||
let ret: Result<()> = ImapResponse::from(&response).into();
|
let ret: Result<()> = ImapResponse::from(&response).into();
|
||||||
if ret.is_ok() {
|
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);
|
let _ = entry.set_is_subscribed(new_val);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rename_folder(&mut self, folder_hash: FolderHash, mut new_path: String) -> Result<Folder> {
|
fn rename_mailbox(
|
||||||
let mut folders = self.folders.write().unwrap();
|
&mut self,
|
||||||
let permissions = folders[&folder_hash].permissions();
|
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 {
|
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);
|
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(
|
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(
|
conn_lck.send_command(
|
||||||
debug!(format!(
|
debug!(format!(
|
||||||
"RENAME \"{}\" \"{}\"",
|
"RENAME \"{}\" \"{}\"",
|
||||||
folders[&folder_hash].imap_path(),
|
mailboxes[&mailbox_hash].imap_path(),
|
||||||
new_path
|
new_path
|
||||||
))
|
))
|
||||||
.as_bytes(),
|
.as_bytes(),
|
||||||
|
@ -641,23 +655,23 @@ impl MailBackend for ImapType {
|
||||||
let new_hash = get_path_hash!(new_path.as_str());
|
let new_hash = get_path_hash!(new_path.as_str());
|
||||||
let ret: Result<()> = ImapResponse::from(&response).into();
|
let ret: Result<()> = ImapResponse::from(&response).into();
|
||||||
ret?;
|
ret?;
|
||||||
folders.clear();
|
mailboxes.clear();
|
||||||
drop(folders);
|
drop(mailboxes);
|
||||||
self.folders().map_err(|err| format!("Mailbox rename was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err))?;
|
self.mailboxes().map_err(|err| format!("Mailbox rename was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err))?;
|
||||||
Ok(BackendFolder::clone(
|
Ok(BackendMailbox::clone(
|
||||||
&self.folders.read().unwrap()[&new_hash],
|
&self.mailboxes.read().unwrap()[&new_hash],
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_folder_permissions(
|
fn set_mailbox_permissions(
|
||||||
&mut self,
|
&mut self,
|
||||||
folder_hash: FolderHash,
|
mailbox_hash: MailboxHash,
|
||||||
_val: crate::backends::FolderPermissions,
|
_val: crate::backends::MailboxPermissions,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let folders = self.folders.write().unwrap();
|
let mailboxes = self.mailboxes.write().unwrap();
|
||||||
let permissions = folders[&folder_hash].permissions();
|
let permissions = mailboxes[&mailbox_hash].permissions();
|
||||||
if !permissions.change_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."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
|
@ -698,7 +712,7 @@ impl ImapType {
|
||||||
|
|
||||||
can_create_flags: Arc::new(Mutex::new(false)),
|
can_create_flags: Arc::new(Mutex::new(false)),
|
||||||
tag_index: Arc::new(RwLock::new(Default::default())),
|
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)),
|
connection: Arc::new(Mutex::new(connection)),
|
||||||
uid_store: Arc::new(UIDStore {
|
uid_store: Arc::new(UIDStore {
|
||||||
uidvalidity: Default::default(),
|
uidvalidity: Default::default(),
|
||||||
|
@ -742,10 +756,10 @@ impl ImapType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn imap_folders(
|
pub fn imap_mailboxes(
|
||||||
connection: &Arc<Mutex<ImapConnection>>,
|
connection: &Arc<Mutex<ImapConnection>>,
|
||||||
) -> Result<FnvHashMap<FolderHash, ImapFolder>> {
|
) -> Result<FnvHashMap<MailboxHash, ImapMailbox>> {
|
||||||
let mut folders: FnvHashMap<FolderHash, ImapFolder> = Default::default();
|
let mut mailboxes: FnvHashMap<MailboxHash, ImapMailbox> = Default::default();
|
||||||
let mut res = String::with_capacity(8 * 1024);
|
let mut res = String::with_capacity(8 * 1024);
|
||||||
let mut conn = connection.lock().unwrap();
|
let mut conn = connection.lock().unwrap();
|
||||||
conn.send_command(b"LIST \"\" \"*\"")?;
|
conn.send_command(b"LIST \"\" \"*\"")?;
|
||||||
|
@ -755,33 +769,33 @@ impl ImapType {
|
||||||
/* Remove "M__ OK .." line */
|
/* Remove "M__ OK .." line */
|
||||||
lines.next_back();
|
lines.next_back();
|
||||||
for l in lines.map(|l| l.trim()) {
|
for l in lines.map(|l| l.trim()) {
|
||||||
if let Ok(mut folder) =
|
if let Ok(mut mailbox) =
|
||||||
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(parent) = folder.parent {
|
if let Some(parent) = mailbox.parent {
|
||||||
if folders.contains_key(&parent) {
|
if mailboxes.contains_key(&parent) {
|
||||||
folders
|
mailboxes
|
||||||
.entry(parent)
|
.entry(parent)
|
||||||
.and_modify(|e| e.children.push(folder.hash));
|
.and_modify(|e| e.children.push(mailbox.hash));
|
||||||
} else {
|
} else {
|
||||||
/* Insert dummy parent entry, populating only the children field. Later
|
/* Insert dummy parent entry, populating only the children field. Later
|
||||||
* when we encounter the parent entry we will swap its children with
|
* when we encounter the parent entry we will swap its children with
|
||||||
* dummy's */
|
* dummy's */
|
||||||
folders.insert(
|
mailboxes.insert(
|
||||||
parent,
|
parent,
|
||||||
ImapFolder {
|
ImapMailbox {
|
||||||
children: vec![folder.hash],
|
children: vec![mailbox.hash],
|
||||||
..ImapFolder::default()
|
..ImapMailbox::default()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if folders.contains_key(&folder.hash) {
|
if mailboxes.contains_key(&mailbox.hash) {
|
||||||
let entry = folders.entry(folder.hash).or_default();
|
let entry = mailboxes.entry(mailbox.hash).or_default();
|
||||||
std::mem::swap(&mut entry.children, &mut folder.children);
|
std::mem::swap(&mut entry.children, &mut mailbox.children);
|
||||||
*entry = folder;
|
*entry = mailbox;
|
||||||
} else {
|
} else {
|
||||||
folders.insert(folder.hash, folder);
|
mailboxes.insert(mailbox.hash, mailbox);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug!("parse error for {:?}", l);
|
debug!("parse error for {:?}", l);
|
||||||
|
@ -795,9 +809,9 @@ impl ImapType {
|
||||||
lines.next_back();
|
lines.next_back();
|
||||||
for l in lines.map(|l| l.trim()) {
|
for l in lines.map(|l| l.trim()) {
|
||||||
if let Ok(subscription) =
|
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 {
|
if subscription.no_select {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -807,7 +821,7 @@ impl ImapType {
|
||||||
debug!("parse error for {:?}", l);
|
debug!("parse error for {:?}", l);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(debug!(folders))
|
Ok(debug!(mailboxes))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn capabilities(&self) -> Vec<String> {
|
pub fn capabilities(&self) -> Vec<String> {
|
||||||
|
@ -823,13 +837,13 @@ impl ImapType {
|
||||||
pub fn search(
|
pub fn search(
|
||||||
&self,
|
&self,
|
||||||
query: String,
|
query: String,
|
||||||
folder_hash: FolderHash,
|
mailbox_hash: MailboxHash,
|
||||||
) -> Result<SmallVec<[EnvelopeHash; 512]>> {
|
) -> 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 response = String::with_capacity(8 * 1024);
|
||||||
let mut conn = self.connection.lock()?;
|
let mut conn = self.connection.lock()?;
|
||||||
conn.send_command(
|
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.read_response(&mut response)?;
|
||||||
conn.send_command(format!("UID SEARCH CHARSET UTF-8 {}", query).as_bytes())?;
|
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
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* 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 crate::error::*;
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct ImapFolder {
|
pub struct ImapMailbox {
|
||||||
pub(super) hash: FolderHash,
|
pub(super) hash: MailboxHash,
|
||||||
pub(super) imap_path: String,
|
pub(super) imap_path: String,
|
||||||
pub(super) path: String,
|
pub(super) path: String,
|
||||||
pub(super) name: String,
|
pub(super) name: String,
|
||||||
pub(super) parent: Option<FolderHash>,
|
pub(super) parent: Option<MailboxHash>,
|
||||||
pub(super) children: Vec<FolderHash>,
|
pub(super) children: Vec<MailboxHash>,
|
||||||
pub separator: u8,
|
pub separator: u8,
|
||||||
pub usage: Arc<RwLock<SpecialUsageMailbox>>,
|
pub usage: Arc<RwLock<SpecialUsageMailbox>>,
|
||||||
pub no_select: bool,
|
pub no_select: bool,
|
||||||
pub is_subscribed: bool,
|
pub is_subscribed: bool,
|
||||||
|
|
||||||
pub permissions: Arc<Mutex<FolderPermissions>>,
|
pub permissions: Arc<Mutex<MailboxPermissions>>,
|
||||||
pub exists: Arc<Mutex<usize>>,
|
pub exists: Arc<Mutex<usize>>,
|
||||||
pub unseen: Arc<Mutex<usize>>,
|
pub unseen: Arc<Mutex<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImapFolder {
|
impl ImapMailbox {
|
||||||
pub fn imap_path(&self) -> &str {
|
pub fn imap_path(&self) -> &str {
|
||||||
&self.imap_path
|
&self.imap_path
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackendFolder for ImapFolder {
|
impl BackendMailbox for ImapMailbox {
|
||||||
fn hash(&self) -> FolderHash {
|
fn hash(&self) -> MailboxHash {
|
||||||
self.hash
|
self.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,11 +65,11 @@ impl BackendFolder for ImapFolder {
|
||||||
self.name = s.to_string();
|
self.name = s.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children(&self) -> &[FolderHash] {
|
fn children(&self) -> &[MailboxHash] {
|
||||||
&self.children
|
&self.children
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone(&self) -> Folder {
|
fn clone(&self) -> Mailbox {
|
||||||
Box::new(std::clone::Clone::clone(self))
|
Box::new(std::clone::Clone::clone(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,11 +77,11 @@ impl BackendFolder for ImapFolder {
|
||||||
*self.usage.read().unwrap()
|
*self.usage.read().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parent(&self) -> Option<FolderHash> {
|
fn parent(&self) -> Option<MailboxHash> {
|
||||||
self.parent
|
self.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
fn permissions(&self) -> FolderPermissions {
|
fn permissions(&self) -> MailboxPermissions {
|
||||||
*self.permissions.lock().unwrap()
|
*self.permissions.lock().unwrap()
|
||||||
}
|
}
|
||||||
fn is_subscribed(&self) -> bool {
|
fn is_subscribed(&self) -> bool {
|
|
@ -34,7 +34,7 @@ pub struct ImapOp {
|
||||||
bytes: Option<String>,
|
bytes: Option<String>,
|
||||||
headers: Option<String>,
|
headers: Option<String>,
|
||||||
body: Option<String>,
|
body: Option<String>,
|
||||||
folder_path: String,
|
mailbox_path: String,
|
||||||
flags: Cell<Option<Flag>>,
|
flags: Cell<Option<Flag>>,
|
||||||
connection: Arc<Mutex<ImapConnection>>,
|
connection: Arc<Mutex<ImapConnection>>,
|
||||||
uid_store: Arc<UIDStore>,
|
uid_store: Arc<UIDStore>,
|
||||||
|
@ -44,7 +44,7 @@ pub struct ImapOp {
|
||||||
impl ImapOp {
|
impl ImapOp {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
uid: usize,
|
uid: usize,
|
||||||
folder_path: String,
|
mailbox_path: String,
|
||||||
connection: Arc<Mutex<ImapConnection>>,
|
connection: Arc<Mutex<ImapConnection>>,
|
||||||
uid_store: Arc<UIDStore>,
|
uid_store: Arc<UIDStore>,
|
||||||
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||||
|
@ -55,7 +55,7 @@ impl ImapOp {
|
||||||
bytes: None,
|
bytes: None,
|
||||||
headers: None,
|
headers: None,
|
||||||
body: None,
|
body: None,
|
||||||
folder_path,
|
mailbox_path,
|
||||||
flags: Cell::new(None),
|
flags: Cell::new(None),
|
||||||
uid_store,
|
uid_store,
|
||||||
tag_index,
|
tag_index,
|
||||||
|
@ -78,7 +78,7 @@ impl BackendOp for ImapOp {
|
||||||
let mut response = String::with_capacity(8 * 1024);
|
let mut response = String::with_capacity(8 * 1024);
|
||||||
{
|
{
|
||||||
let mut conn = self.connection.lock().unwrap();
|
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.read_response(&mut response)?;
|
||||||
conn.send_command(format!("UID FETCH {} (FLAGS RFC822)", self.uid).as_bytes())?;
|
conn.send_command(format!("UID FETCH {} (FLAGS RFC822)", self.uid).as_bytes())?;
|
||||||
conn.read_response(&mut response)?;
|
conn.read_response(&mut response)?;
|
||||||
|
@ -116,7 +116,7 @@ impl BackendOp for ImapOp {
|
||||||
} else {
|
} else {
|
||||||
let mut response = String::with_capacity(8 * 1024);
|
let mut response = String::with_capacity(8 * 1024);
|
||||||
let mut conn = self.connection.lock().unwrap();
|
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();
|
.unwrap();
|
||||||
conn.read_response(&mut response).unwrap();
|
conn.read_response(&mut response).unwrap();
|
||||||
conn.send_command(format!("UID FETCH {} FLAGS", self.uid).as_bytes())
|
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 response = String::with_capacity(8 * 1024);
|
||||||
let mut conn = self.connection.lock().unwrap();
|
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.read_response(&mut response)?;
|
||||||
debug!(&response);
|
debug!(&response);
|
||||||
conn.send_command(
|
conn.send_command(
|
||||||
|
@ -190,7 +190,7 @@ impl BackendOp for ImapOp {
|
||||||
fn set_tag(&mut self, envelope: &mut Envelope, tag: String, value: bool) -> Result<()> {
|
fn set_tag(&mut self, envelope: &mut Envelope, tag: String, value: bool) -> Result<()> {
|
||||||
let mut response = String::with_capacity(8 * 1024);
|
let mut response = String::with_capacity(8 * 1024);
|
||||||
let mut conn = self.connection.lock().unwrap();
|
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.read_response(&mut response)?;
|
||||||
conn.send_command(
|
conn.send_command(
|
||||||
format!(
|
format!(
|
||||||
|
|
|
@ -213,7 +213,7 @@ macro_rules! dbg_dmp (
|
||||||
* LIST (\HasChildren) "." INBOX
|
* LIST (\HasChildren) "." INBOX
|
||||||
*/
|
*/
|
||||||
named!(
|
named!(
|
||||||
pub list_folder_result<ImapFolder>,
|
pub list_mailbox_result<ImapMailbox>,
|
||||||
do_parse!(
|
do_parse!(
|
||||||
ws!(alt_complete!(tag!("* LIST (") | tag!("* LSUB (")))
|
ws!(alt_complete!(tag!("* LIST (") | tag!("* LSUB (")))
|
||||||
>> properties: take_until!(&b")"[0..])
|
>> properties: take_until!(&b")"[0..])
|
||||||
|
@ -223,7 +223,7 @@ named!(
|
||||||
>> path: alt_complete!(delimited!(tag!("\""), is_not!("\""), tag!("\"")) | call!(rest))
|
>> path: alt_complete!(delimited!(tag!("\""), is_not!("\""), tag!("\"")) | call!(rest))
|
||||||
>> ({
|
>> ({
|
||||||
let separator: u8 = separator[0];
|
let separator: u8 = separator[0];
|
||||||
let mut f = ImapFolder::default();
|
let mut f = ImapMailbox::default();
|
||||||
f.no_select = false;
|
f.no_select = false;
|
||||||
f.is_subscribed = false;
|
f.is_subscribed = false;
|
||||||
for p in properties.split(|&b| b == b' ') {
|
for p in properties.split(|&b| b == b' ') {
|
||||||
|
|
|
@ -28,20 +28,20 @@ pub struct ImapWatchKit {
|
||||||
pub is_online: Arc<Mutex<(Instant, Result<()>)>>,
|
pub is_online: Arc<Mutex<(Instant, Result<()>)>>,
|
||||||
pub main_conn: Arc<Mutex<ImapConnection>>,
|
pub main_conn: Arc<Mutex<ImapConnection>>,
|
||||||
pub uid_store: Arc<UIDStore>,
|
pub uid_store: Arc<UIDStore>,
|
||||||
pub folders: Arc<RwLock<FnvHashMap<FolderHash, ImapFolder>>>,
|
pub mailboxes: Arc<RwLock<FnvHashMap<MailboxHash, ImapMailbox>>>,
|
||||||
pub sender: RefreshEventConsumer,
|
pub sender: RefreshEventConsumer,
|
||||||
pub work_context: WorkContext,
|
pub work_context: WorkContext,
|
||||||
pub tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
pub tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! exit_on_error {
|
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 {
|
$(if let Err(e) = $result {
|
||||||
debug!("failure: {}", e.to_string());
|
debug!("failure: {}", e.to_string());
|
||||||
$work_context.set_status.send(($thread_id, e.to_string())).unwrap();
|
$work_context.set_status.send(($thread_id, e.to_string())).unwrap();
|
||||||
$work_context.finished.send($thread_id).unwrap();
|
$work_context.finished.send($thread_id).unwrap();
|
||||||
$sender.send(RefreshEvent {
|
$sender.send(RefreshEvent {
|
||||||
hash: $folder_hash,
|
hash: $mailbox_hash,
|
||||||
kind: RefreshEventKind::Failure(e.clone()),
|
kind: RefreshEventKind::Failure(e.clone()),
|
||||||
});
|
});
|
||||||
Err(e)
|
Err(e)
|
||||||
|
@ -56,7 +56,7 @@ pub fn poll_with_examine(kit: ImapWatchKit) -> Result<()> {
|
||||||
mut conn,
|
mut conn,
|
||||||
main_conn,
|
main_conn,
|
||||||
uid_store,
|
uid_store,
|
||||||
folders,
|
mailboxes,
|
||||||
sender,
|
sender,
|
||||||
work_context,
|
work_context,
|
||||||
tag_index,
|
tag_index,
|
||||||
|
@ -76,17 +76,17 @@ pub fn poll_with_examine(kit: ImapWatchKit) -> Result<()> {
|
||||||
.send((thread_id, "sleeping...".to_string()))
|
.send((thread_id, "sleeping...".to_string()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
std::thread::sleep(std::time::Duration::from_millis(5 * 60 * 1000));
|
std::thread::sleep(std::time::Duration::from_millis(5 * 60 * 1000));
|
||||||
let folders = folders.read().unwrap();
|
let mailboxes = mailboxes.read().unwrap();
|
||||||
for folder in folders.values() {
|
for mailbox in mailboxes.values() {
|
||||||
work_context
|
work_context
|
||||||
.set_status
|
.set_status
|
||||||
.send((
|
.send((
|
||||||
thread_id,
|
thread_id,
|
||||||
format!("examining `{}` for updates...", folder.path()),
|
format!("examining `{}` for updates...", mailbox.path()),
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
examine_updates(
|
examine_updates(
|
||||||
folder,
|
mailbox,
|
||||||
&sender,
|
&sender,
|
||||||
&mut conn,
|
&mut conn,
|
||||||
&uid_store,
|
&uid_store,
|
||||||
|
@ -109,7 +109,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
is_online,
|
is_online,
|
||||||
main_conn,
|
main_conn,
|
||||||
uid_store,
|
uid_store,
|
||||||
folders,
|
mailboxes,
|
||||||
sender,
|
sender,
|
||||||
work_context,
|
work_context,
|
||||||
tag_index,
|
tag_index,
|
||||||
|
@ -122,16 +122,16 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
}
|
}
|
||||||
conn.connect()?;
|
conn.connect()?;
|
||||||
let thread_id: std::thread::ThreadId = std::thread::current().id();
|
let thread_id: std::thread::ThreadId = std::thread::current().id();
|
||||||
let folder: ImapFolder = match folders
|
let mailbox: ImapMailbox = match mailboxes
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.values()
|
.values()
|
||||||
.find(|f| f.parent.is_none() && (f.special_usage() == SpecialUsageMailbox::Inbox))
|
.find(|f| f.parent.is_none() && (f.special_usage() == SpecialUsageMailbox::Inbox))
|
||||||
.map(std::clone::Clone::clone)
|
.map(std::clone::Clone::clone)
|
||||||
{
|
{
|
||||||
Some(folder) => folder,
|
Some(mailbox) => mailbox,
|
||||||
None => {
|
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());
|
debug!("failure: {}", err.to_string());
|
||||||
work_context
|
work_context
|
||||||
.set_status
|
.set_status
|
||||||
|
@ -144,28 +144,28 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let folder_hash = folder.hash();
|
let mailbox_hash = mailbox.hash();
|
||||||
let mut response = String::with_capacity(8 * 1024);
|
let mut response = String::with_capacity(8 * 1024);
|
||||||
exit_on_error!(
|
exit_on_error!(
|
||||||
sender,
|
sender,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
work_context,
|
work_context,
|
||||||
thread_id,
|
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)
|
conn.read_response(&mut response)
|
||||||
);
|
);
|
||||||
debug!("select response {}", &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) {
|
*prev_exists = match protocol_parser::select_response(&response) {
|
||||||
Ok(ok) => {
|
Ok(ok) => {
|
||||||
{
|
{
|
||||||
let mut uidvalidities = uid_store.uidvalidity.lock().unwrap();
|
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 {
|
if *v != ok.uidvalidity {
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: RefreshEventKind::Rescan,
|
kind: RefreshEventKind::Rescan,
|
||||||
});
|
});
|
||||||
*prev_exists = 0;
|
*prev_exists = 0;
|
||||||
|
@ -176,15 +176,15 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: RefreshEventKind::Rescan,
|
kind: RefreshEventKind::Rescan,
|
||||||
});
|
});
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
||||||
"Unknown mailbox: {} {}",
|
"Unknown mailbox: {} {}",
|
||||||
folder.path(),
|
mailbox.path(),
|
||||||
folder_hash
|
mailbox_hash
|
||||||
))),
|
))),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -200,7 +200,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
}
|
}
|
||||||
exit_on_error!(
|
exit_on_error!(
|
||||||
sender,
|
sender,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
work_context,
|
work_context,
|
||||||
thread_id,
|
thread_id,
|
||||||
conn.send_command(b"IDLE")
|
conn.send_command(b"IDLE")
|
||||||
|
@ -214,14 +214,14 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
let mut watch = std::time::Instant::now();
|
let mut watch = std::time::Instant::now();
|
||||||
/* duration interval to send heartbeat */
|
/* duration interval to send heartbeat */
|
||||||
let _26_mins = std::time::Duration::from_secs(26 * 60);
|
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);
|
let _5_mins = std::time::Duration::from_secs(5 * 60);
|
||||||
while let Some(line) = iter.next() {
|
while let Some(line) = iter.next() {
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
if now.duration_since(beat) >= _26_mins {
|
if now.duration_since(beat) >= _26_mins {
|
||||||
exit_on_error!(
|
exit_on_error!(
|
||||||
sender,
|
sender,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
work_context,
|
work_context,
|
||||||
thread_id,
|
thread_id,
|
||||||
iter.conn.set_nonblocking(true)
|
iter.conn.set_nonblocking(true)
|
||||||
|
@ -237,21 +237,21 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
if now.duration_since(watch) >= _5_mins {
|
if now.duration_since(watch) >= _5_mins {
|
||||||
/* Time to poll all inboxes */
|
/* Time to poll all inboxes */
|
||||||
let mut conn = main_conn.lock().unwrap();
|
let mut conn = main_conn.lock().unwrap();
|
||||||
for folder in folders.read().unwrap().values() {
|
for mailbox in mailboxes.read().unwrap().values() {
|
||||||
work_context
|
work_context
|
||||||
.set_status
|
.set_status
|
||||||
.send((
|
.send((
|
||||||
thread_id,
|
thread_id,
|
||||||
format!("examining `{}` for updates...", folder.path()),
|
format!("examining `{}` for updates...", mailbox.path()),
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
exit_on_error!(
|
exit_on_error!(
|
||||||
sender,
|
sender,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
work_context,
|
work_context,
|
||||||
thread_id,
|
thread_id,
|
||||||
examine_updates(
|
examine_updates(
|
||||||
folder,
|
mailbox,
|
||||||
&sender,
|
&sender,
|
||||||
&mut conn,
|
&mut conn,
|
||||||
&uid_store,
|
&uid_store,
|
||||||
|
@ -279,7 +279,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
/* UID SEARCH RECENT */
|
/* UID SEARCH RECENT */
|
||||||
exit_on_error!(
|
exit_on_error!(
|
||||||
sender,
|
sender,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
work_context,
|
work_context,
|
||||||
thread_id,
|
thread_id,
|
||||||
conn.send_command(b"EXAMINE INBOX")
|
conn.send_command(b"EXAMINE INBOX")
|
||||||
|
@ -297,7 +297,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
Ok(v) => {
|
Ok(v) => {
|
||||||
exit_on_error!(
|
exit_on_error!(
|
||||||
sender,
|
sender,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
work_context,
|
work_context,
|
||||||
thread_id,
|
thread_id,
|
||||||
conn.send_command(
|
conn.send_command(
|
||||||
|
@ -331,13 +331,13 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
.hash_index
|
.hash_index
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(env.hash(), (uid, folder_hash));
|
.insert(env.hash(), (uid, mailbox_hash));
|
||||||
uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
|
uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
|
||||||
debug!(
|
debug!(
|
||||||
"Create event {} {} {}",
|
"Create event {} {} {}",
|
||||||
env.hash(),
|
env.hash(),
|
||||||
env.subject(),
|
env.subject(),
|
||||||
folder.path(),
|
mailbox.path(),
|
||||||
);
|
);
|
||||||
if let Some((_, keywords)) = flags {
|
if let Some((_, keywords)) = flags {
|
||||||
let mut tag_lck = tag_index.write().unwrap();
|
let mut tag_lck = tag_index.write().unwrap();
|
||||||
|
@ -350,12 +350,12 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !env.is_seen() {
|
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 {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: Create(Box::new(env)),
|
kind: Create(Box::new(env)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -390,7 +390,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
let mut conn = main_conn.lock().unwrap();
|
let mut conn = main_conn.lock().unwrap();
|
||||||
/* UID FETCH ALL UID, cross-ref, then FETCH difference headers
|
/* 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);
|
debug!("exists {}", n);
|
||||||
work_context
|
work_context
|
||||||
.set_status
|
.set_status
|
||||||
|
@ -400,14 +400,14 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
"got `{} EXISTS` notification (EXISTS was previously {} for {}",
|
"got `{} EXISTS` notification (EXISTS was previously {} for {}",
|
||||||
n,
|
n,
|
||||||
*prev_exists,
|
*prev_exists,
|
||||||
folder.path()
|
mailbox.path()
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if n > *prev_exists {
|
if n > *prev_exists {
|
||||||
exit_on_error!(
|
exit_on_error!(
|
||||||
sender,
|
sender,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
work_context,
|
work_context,
|
||||||
thread_id,
|
thread_id,
|
||||||
conn.send_command(b"EXAMINE INBOX")
|
conn.send_command(b"EXAMINE INBOX")
|
||||||
|
@ -450,7 +450,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
.hash_index
|
.hash_index
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(env.hash(), (uid, folder_hash));
|
.insert(env.hash(), (uid, mailbox_hash));
|
||||||
uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
|
uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
|
||||||
if let Some((_, keywords)) = flags {
|
if let Some((_, keywords)) = flags {
|
||||||
let mut tag_lck = tag_index.write().unwrap();
|
let mut tag_lck = tag_index.write().unwrap();
|
||||||
|
@ -466,13 +466,13 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
"Create event {} {} {}",
|
"Create event {} {} {}",
|
||||||
env.hash(),
|
env.hash(),
|
||||||
env.subject(),
|
env.subject(),
|
||||||
folder.path(),
|
mailbox.path(),
|
||||||
);
|
);
|
||||||
if !env.is_seen() {
|
if !env.is_seen() {
|
||||||
*folder.unseen.lock().unwrap() += 1;
|
*mailbox.unseen.lock().unwrap() += 1;
|
||||||
}
|
}
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: Create(Box::new(env)),
|
kind: Create(Box::new(env)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -507,7 +507,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
work_context.finished.send(thread_id).unwrap();
|
work_context.finished.send(thread_id).unwrap();
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
||||||
"IDLE connection dropped: {}",
|
"IDLE connection dropped: {}",
|
||||||
&err
|
&err
|
||||||
|
@ -517,7 +517,7 @@ pub fn idle(kit: ImapWatchKit) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn examine_updates(
|
pub fn examine_updates(
|
||||||
folder: &ImapFolder,
|
mailbox: &ImapMailbox,
|
||||||
sender: &RefreshEventConsumer,
|
sender: &RefreshEventConsumer,
|
||||||
conn: &mut ImapConnection,
|
conn: &mut ImapConnection,
|
||||||
uid_store: &Arc<UIDStore>,
|
uid_store: &Arc<UIDStore>,
|
||||||
|
@ -525,15 +525,15 @@ pub fn examine_updates(
|
||||||
tag_index: &Arc<RwLock<BTreeMap<u64, String>>>,
|
tag_index: &Arc<RwLock<BTreeMap<u64, String>>>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let thread_id: std::thread::ThreadId = std::thread::current().id();
|
let thread_id: std::thread::ThreadId = std::thread::current().id();
|
||||||
let folder_hash = folder.hash();
|
let mailbox_hash = mailbox.hash();
|
||||||
debug!("examining folder {} {}", folder_hash, folder.path());
|
debug!("examining mailbox {} {}", mailbox_hash, mailbox.path());
|
||||||
let mut response = String::with_capacity(8 * 1024);
|
let mut response = String::with_capacity(8 * 1024);
|
||||||
exit_on_error!(
|
exit_on_error!(
|
||||||
sender,
|
sender,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
work_context,
|
work_context,
|
||||||
thread_id,
|
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)
|
conn.read_response(&mut response)
|
||||||
);
|
);
|
||||||
match protocol_parser::select_response(&response) {
|
match protocol_parser::select_response(&response) {
|
||||||
|
@ -542,10 +542,10 @@ pub fn examine_updates(
|
||||||
{
|
{
|
||||||
let mut uidvalidities = uid_store.uidvalidity.lock().unwrap();
|
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 {
|
if *v != ok.uidvalidity {
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: RefreshEventKind::Rescan,
|
kind: RefreshEventKind::Rescan,
|
||||||
});
|
});
|
||||||
uid_store.uid_index.lock().unwrap().clear();
|
uid_store.uid_index.lock().unwrap().clear();
|
||||||
|
@ -555,27 +555,27 @@ pub fn examine_updates(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: RefreshEventKind::Rescan,
|
kind: RefreshEventKind::Rescan,
|
||||||
});
|
});
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
||||||
"Unknown mailbox: {} {}",
|
"Unknown mailbox: {} {}",
|
||||||
folder.path(),
|
mailbox.path(),
|
||||||
folder_hash
|
mailbox_hash
|
||||||
))),
|
))),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut prev_exists = folder.exists.lock().unwrap();
|
let mut prev_exists = mailbox.exists.lock().unwrap();
|
||||||
let n = ok.exists;
|
let n = ok.exists;
|
||||||
if ok.recent > 0 {
|
if ok.recent > 0 {
|
||||||
{
|
{
|
||||||
/* UID SEARCH RECENT */
|
/* UID SEARCH RECENT */
|
||||||
exit_on_error!(
|
exit_on_error!(
|
||||||
sender,
|
sender,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
work_context,
|
work_context,
|
||||||
thread_id,
|
thread_id,
|
||||||
conn.send_command(b"UID SEARCH RECENT")
|
conn.send_command(b"UID SEARCH RECENT")
|
||||||
|
@ -591,7 +591,7 @@ pub fn examine_updates(
|
||||||
Ok(v) => {
|
Ok(v) => {
|
||||||
exit_on_error!(
|
exit_on_error!(
|
||||||
sender,
|
sender,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
work_context,
|
work_context,
|
||||||
thread_id,
|
thread_id,
|
||||||
conn.send_command(
|
conn.send_command(
|
||||||
|
@ -615,7 +615,7 @@ pub fn examine_updates(
|
||||||
.hash_index
|
.hash_index
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(env.hash(), (uid, folder_hash));
|
.insert(env.hash(), (uid, mailbox_hash));
|
||||||
uid_store
|
uid_store
|
||||||
.uid_index
|
.uid_index
|
||||||
.lock()
|
.lock()
|
||||||
|
@ -625,7 +625,7 @@ pub fn examine_updates(
|
||||||
"Create event {} {} {}",
|
"Create event {} {} {}",
|
||||||
env.hash(),
|
env.hash(),
|
||||||
env.subject(),
|
env.subject(),
|
||||||
folder.path(),
|
mailbox.path(),
|
||||||
);
|
);
|
||||||
if let Some((_, keywords)) = flags {
|
if let Some((_, keywords)) = flags {
|
||||||
let mut tag_lck = tag_index.write().unwrap();
|
let mut tag_lck = tag_index.write().unwrap();
|
||||||
|
@ -638,10 +638,10 @@ pub fn examine_updates(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !env.is_seen() {
|
if !env.is_seen() {
|
||||||
*folder.unseen.lock().unwrap() += 1;
|
*mailbox.unseen.lock().unwrap() += 1;
|
||||||
}
|
}
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: Create(Box::new(env)),
|
kind: Create(Box::new(env)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -667,7 +667,7 @@ pub fn examine_updates(
|
||||||
debug!("exists {}", n);
|
debug!("exists {}", n);
|
||||||
exit_on_error!(
|
exit_on_error!(
|
||||||
sender,
|
sender,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
work_context,
|
work_context,
|
||||||
thread_id,
|
thread_id,
|
||||||
conn.send_command(
|
conn.send_command(
|
||||||
|
@ -693,7 +693,7 @@ pub fn examine_updates(
|
||||||
.hash_index
|
.hash_index
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(env.hash(), (uid, folder_hash));
|
.insert(env.hash(), (uid, mailbox_hash));
|
||||||
uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
|
uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
|
||||||
if let Some((_, keywords)) = flags {
|
if let Some((_, keywords)) = flags {
|
||||||
let mut tag_lck = tag_index.write().unwrap();
|
let mut tag_lck = tag_index.write().unwrap();
|
||||||
|
@ -709,13 +709,13 @@ pub fn examine_updates(
|
||||||
"Create event {} {} {}",
|
"Create event {} {} {}",
|
||||||
env.hash(),
|
env.hash(),
|
||||||
env.subject(),
|
env.subject(),
|
||||||
folder.path(),
|
mailbox.path(),
|
||||||
);
|
);
|
||||||
if !env.is_seen() {
|
if !env.is_seen() {
|
||||||
*folder.unseen.lock().unwrap() += 1;
|
*mailbox.unseen.lock().unwrap() += 1;
|
||||||
}
|
}
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: Create(Box::new(env)),
|
kind: Create(Box::new(env)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
|
|
||||||
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
||||||
use crate::backends::BackendOp;
|
use crate::backends::BackendOp;
|
||||||
use crate::backends::FolderHash;
|
use crate::backends::MailboxHash;
|
||||||
use crate::backends::{BackendFolder, Folder, MailBackend, RefreshEventConsumer};
|
use crate::backends::{BackendMailbox, MailBackend, Mailbox, RefreshEventConsumer};
|
||||||
use crate::conf::AccountSettings;
|
use crate::conf::AccountSettings;
|
||||||
use crate::email::*;
|
use crate::email::*;
|
||||||
use crate::error::{MeliError, Result};
|
use crate::error::{MeliError, Result};
|
||||||
|
@ -71,8 +71,8 @@ use rfc8620::*;
|
||||||
pub mod objects;
|
pub mod objects;
|
||||||
use objects::*;
|
use objects::*;
|
||||||
|
|
||||||
pub mod folder;
|
pub mod mailbox;
|
||||||
use folder::*;
|
use mailbox::*;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct EnvelopeCache {
|
pub struct EnvelopeCache {
|
||||||
|
@ -189,7 +189,7 @@ pub struct JmapType {
|
||||||
connection: Arc<JmapConnection>,
|
connection: Arc<JmapConnection>,
|
||||||
store: Arc<RwLock<Store>>,
|
store: Arc<RwLock<Store>>,
|
||||||
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||||
folders: Arc<RwLock<FnvHashMap<FolderHash, JmapFolder>>>,
|
mailboxes: Arc<RwLock<FnvHashMap<MailboxHash, JmapMailbox>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MailBackend for JmapType {
|
impl MailBackend for JmapType {
|
||||||
|
@ -202,18 +202,18 @@ impl MailBackend for JmapType {
|
||||||
if Instant::now().duration_since(self.online.lock().unwrap().0)
|
if Instant::now().duration_since(self.online.lock().unwrap().0)
|
||||||
>= std::time::Duration::new(2, 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 mut w = AsyncBuilder::new();
|
||||||
let folders = self.folders.clone();
|
let mailboxes = self.mailboxes.clone();
|
||||||
let store = self.store.clone();
|
let store = self.store.clone();
|
||||||
let tag_index = self.tag_index.clone();
|
let tag_index = self.tag_index.clone();
|
||||||
let connection = self.connection.clone();
|
let connection = self.connection.clone();
|
||||||
let folder_hash = folder.hash();
|
let mailbox_hash = mailbox.hash();
|
||||||
let handle = {
|
let handle = {
|
||||||
let tx = w.tx();
|
let tx = w.tx();
|
||||||
let closure = move |_work_context| {
|
let closure = move |_work_context| {
|
||||||
|
@ -221,7 +221,7 @@ impl MailBackend for JmapType {
|
||||||
&connection,
|
&connection,
|
||||||
&store,
|
&store,
|
||||||
&tag_index,
|
&tag_index,
|
||||||
&folders.read().unwrap()[&folder_hash],
|
&mailboxes.read().unwrap()[&mailbox_hash],
|
||||||
)))
|
)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
tx.send(AsyncStatus::Finished).unwrap();
|
tx.send(AsyncStatus::Finished).unwrap();
|
||||||
|
@ -239,19 +239,19 @@ impl MailBackend for JmapType {
|
||||||
Err(MeliError::from("sadfsa"))
|
Err(MeliError::from("sadfsa"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
|
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||||
if self.folders.read().unwrap().is_empty() {
|
if self.mailboxes.read().unwrap().is_empty() {
|
||||||
let folders = std::dbg!(protocol::get_mailboxes(&self.connection))?;
|
let mailboxes = std::dbg!(protocol::get_mailboxes(&self.connection))?;
|
||||||
*self.folders.write().unwrap() = folders;
|
*self.mailboxes.write().unwrap() = mailboxes;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self
|
Ok(self
|
||||||
.folders
|
.mailboxes
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(_, f)| f.is_subscribed)
|
.filter(|(_, f)| f.is_subscribed)
|
||||||
.map(|(&h, f)| (h, BackendFolder::clone(f) as Folder))
|
.map(|(&h, f)| (h, BackendMailbox::clone(f) as Mailbox))
|
||||||
.collect())
|
.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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,7 +291,7 @@ impl JmapType {
|
||||||
connection: Arc::new(JmapConnection::new(&server_conf, online.clone())?),
|
connection: Arc::new(JmapConnection::new(&server_conf, online.clone())?),
|
||||||
store: Arc::new(RwLock::new(Store::default())),
|
store: Arc::new(RwLock::new(Store::default())),
|
||||||
tag_index: Arc::new(RwLock::new(Default::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(),
|
account_name: s.name.clone(),
|
||||||
online,
|
online,
|
||||||
is_subscribed: Arc::new(IsSubscribedFn(is_subscribed)),
|
is_subscribed: Arc::new(IsSubscribedFn(is_subscribed)),
|
||||||
|
|
|
@ -20,15 +20,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::backends::{FolderPermissions, SpecialUsageMailbox};
|
use crate::backends::{MailboxPermissions, SpecialUsageMailbox};
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct JmapFolder {
|
pub struct JmapMailbox {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub path: String,
|
pub path: String,
|
||||||
pub hash: FolderHash,
|
pub hash: MailboxHash,
|
||||||
pub v: Vec<FolderHash>,
|
pub v: Vec<MailboxHash>,
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub is_subscribed: bool,
|
pub is_subscribed: bool,
|
||||||
pub my_rights: JmapRights,
|
pub my_rights: JmapRights,
|
||||||
|
@ -42,8 +42,8 @@ pub struct JmapFolder {
|
||||||
pub usage: Arc<RwLock<SpecialUsageMailbox>>,
|
pub usage: Arc<RwLock<SpecialUsageMailbox>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackendFolder for JmapFolder {
|
impl BackendMailbox for JmapMailbox {
|
||||||
fn hash(&self) -> FolderHash {
|
fn hash(&self) -> MailboxHash {
|
||||||
self.hash
|
self.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,20 +57,20 @@ impl BackendFolder for JmapFolder {
|
||||||
|
|
||||||
fn change_name(&mut self, _s: &str) {}
|
fn change_name(&mut self, _s: &str) {}
|
||||||
|
|
||||||
fn clone(&self) -> Folder {
|
fn clone(&self) -> Mailbox {
|
||||||
Box::new(std::clone::Clone::clone(self))
|
Box::new(std::clone::Clone::clone(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children(&self) -> &[FolderHash] {
|
fn children(&self) -> &[MailboxHash] {
|
||||||
&self.v
|
&self.v
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parent(&self) -> Option<FolderHash> {
|
fn parent(&self) -> Option<MailboxHash> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn permissions(&self) -> FolderPermissions {
|
fn permissions(&self) -> MailboxPermissions {
|
||||||
FolderPermissions::default()
|
MailboxPermissions::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn special_usage(&self) -> SpecialUsageMailbox {
|
fn special_usage(&self) -> SpecialUsageMailbox {
|
||||||
|
@ -83,7 +83,7 @@ impl BackendFolder for JmapFolder {
|
||||||
Some("sent") => SpecialUsageMailbox::Sent,
|
Some("sent") => SpecialUsageMailbox::Sent,
|
||||||
Some(other) => {
|
Some(other) => {
|
||||||
debug!(
|
debug!(
|
||||||
"unknown JMAP mailbox role for folder {}: {}",
|
"unknown JMAP mailbox role for mailbox {}: {}",
|
||||||
self.path(),
|
self.path(),
|
||||||
other
|
other
|
||||||
);
|
);
|
|
@ -394,7 +394,7 @@ pub struct EmailQueryResponse {
|
||||||
pub struct EmailQuery {
|
pub struct EmailQuery {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub query_call: Query<EmailFilterCondition, EmailObject>,
|
pub query_call: Query<EmailFilterCondition, EmailObject>,
|
||||||
//pub filter: EmailFilterCondition, /* "inMailboxes": [ folder.id ] },*/
|
//pub filter: EmailFilterCondition, /* "inMailboxes": [ mailbox.id ] },*/
|
||||||
pub collapse_threads: bool,
|
pub collapse_threads: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::folder::JmapFolder;
|
use super::mailbox::JmapMailbox;
|
||||||
use super::*;
|
use super::*;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use serde_json::{json, Value};
|
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 seq = get_request_no!(conn.request_no);
|
||||||
let res = conn
|
let res = conn
|
||||||
.client
|
.client
|
||||||
|
@ -141,7 +141,7 @@ pub fn get_mailboxes(conn: &JmapConnection) -> Result<FnvHashMap<FolderHash, Jma
|
||||||
let hash = crate::get_path_hash!(&name);
|
let hash = crate::get_path_hash!(&name);
|
||||||
(
|
(
|
||||||
hash,
|
hash,
|
||||||
JmapFolder {
|
JmapMailbox {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
hash,
|
hash,
|
||||||
path: name,
|
path: name,
|
||||||
|
@ -170,12 +170,12 @@ pub struct JsonResponse<'a> {
|
||||||
method_responses: Vec<MethodResponse<'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(
|
let email_call: EmailQuery = EmailQuery::new(
|
||||||
Query::new()
|
Query::new()
|
||||||
.account_id(conn.mail_account_id().to_string())
|
.account_id(conn.mail_account_id().to_string())
|
||||||
.filter(Some(
|
.filter(Some(
|
||||||
EmailFilterCondition::new().in_mailbox(Some(folder.id.clone())),
|
EmailFilterCondition::new().in_mailbox(Some(mailbox.id.clone())),
|
||||||
))
|
))
|
||||||
.position(0),
|
.position(0),
|
||||||
)
|
)
|
||||||
|
@ -241,13 +241,13 @@ pub fn get(
|
||||||
conn: &JmapConnection,
|
conn: &JmapConnection,
|
||||||
store: &Arc<RwLock<Store>>,
|
store: &Arc<RwLock<Store>>,
|
||||||
tag_index: &Arc<RwLock<BTreeMap<u64, String>>>,
|
tag_index: &Arc<RwLock<BTreeMap<u64, String>>>,
|
||||||
folder: &JmapFolder,
|
mailbox: &JmapMailbox,
|
||||||
) -> Result<Vec<Envelope>> {
|
) -> Result<Vec<Envelope>> {
|
||||||
let email_query_call: EmailQuery = EmailQuery::new(
|
let email_query_call: EmailQuery = EmailQuery::new(
|
||||||
Query::new()
|
Query::new()
|
||||||
.account_id(conn.mail_account_id().to_string())
|
.account_id(conn.mail_account_id().to_string())
|
||||||
.filter(Some(
|
.filter(Some(
|
||||||
EmailFilterCondition::new().in_mailbox(Some(folder.id.clone())),
|
EmailFilterCondition::new().in_mailbox(Some(mailbox.id.clone())),
|
||||||
))
|
))
|
||||||
.position(0),
|
.position(0),
|
||||||
)
|
)
|
||||||
|
|
|
@ -39,7 +39,7 @@ use std::sync::{Arc, Mutex};
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MaildirOp {
|
pub struct MaildirOp {
|
||||||
hash_index: HashIndexes,
|
hash_index: HashIndexes,
|
||||||
folder_hash: FolderHash,
|
mailbox_hash: MailboxHash,
|
||||||
hash: EnvelopeHash,
|
hash: EnvelopeHash,
|
||||||
slice: Option<Mmap>,
|
slice: Option<Mmap>,
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ impl Clone for MaildirOp {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
MaildirOp {
|
MaildirOp {
|
||||||
hash_index: self.hash_index.clone(),
|
hash_index: self.hash_index.clone(),
|
||||||
folder_hash: self.folder_hash,
|
mailbox_hash: self.mailbox_hash,
|
||||||
hash: self.hash,
|
hash: self.hash,
|
||||||
slice: None,
|
slice: None,
|
||||||
}
|
}
|
||||||
|
@ -56,18 +56,18 @@ impl Clone for MaildirOp {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
MaildirOp {
|
||||||
hash_index,
|
hash_index,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
hash,
|
hash,
|
||||||
slice: None,
|
slice: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn path(&self) -> PathBuf {
|
fn path(&self) -> PathBuf {
|
||||||
let map = self.hash_index.lock().unwrap();
|
let map = self.hash_index.lock().unwrap();
|
||||||
let map = &map[&self.folder_hash];
|
let map = &map[&self.mailbox_hash];
|
||||||
debug!("looking for {} in {} map", self.hash, self.folder_hash);
|
debug!("looking for {} in {} map", self.hash, self.mailbox_hash);
|
||||||
if !map.contains_key(&self.hash) {
|
if !map.contains_key(&self.hash) {
|
||||||
debug!("doesn't contain it though len = {}\n{:#?}", map.len(), map);
|
debug!("doesn't contain it though len = {}\n{:#?}", map.len(), map);
|
||||||
for e in map.iter() {
|
for e in map.iter() {
|
||||||
|
@ -155,7 +155,7 @@ impl<'a> BackendOp for MaildirOp {
|
||||||
let new_name: PathBuf = new_name.into();
|
let new_name: PathBuf = new_name.into();
|
||||||
let hash_index = self.hash_index.clone();
|
let hash_index = self.hash_index.clone();
|
||||||
let mut map = hash_index.lock().unwrap();
|
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()));
|
map.entry(old_hash).or_default().modified = Some(PathMod::Path(new_name.clone()));
|
||||||
|
|
||||||
debug!("renaming {:?} to {:?}", path, new_name);
|
debug!("renaming {:?} to {:?}", path, new_name);
|
||||||
|
@ -170,37 +170,37 @@ impl<'a> BackendOp for MaildirOp {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct MaildirFolder {
|
pub struct MaildirMailbox {
|
||||||
hash: FolderHash,
|
hash: MailboxHash,
|
||||||
name: String,
|
name: String,
|
||||||
fs_path: PathBuf,
|
fs_path: PathBuf,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
parent: Option<FolderHash>,
|
parent: Option<MailboxHash>,
|
||||||
children: Vec<FolderHash>,
|
children: Vec<MailboxHash>,
|
||||||
pub usage: Arc<RwLock<SpecialUsageMailbox>>,
|
pub usage: Arc<RwLock<SpecialUsageMailbox>>,
|
||||||
pub is_subscribed: bool,
|
pub is_subscribed: bool,
|
||||||
permissions: FolderPermissions,
|
permissions: MailboxPermissions,
|
||||||
pub total: Arc<Mutex<usize>>,
|
pub total: Arc<Mutex<usize>>,
|
||||||
pub unseen: Arc<Mutex<usize>>,
|
pub unseen: Arc<Mutex<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaildirFolder {
|
impl MaildirMailbox {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
path: String,
|
path: String,
|
||||||
file_name: String,
|
file_name: String,
|
||||||
parent: Option<FolderHash>,
|
parent: Option<MailboxHash>,
|
||||||
children: Vec<FolderHash>,
|
children: Vec<MailboxHash>,
|
||||||
settings: &AccountSettings,
|
settings: &AccountSettings,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
let pathbuf = PathBuf::from(&path);
|
let pathbuf = PathBuf::from(&path);
|
||||||
let mut h = DefaultHasher::new();
|
let mut h = DefaultHasher::new();
|
||||||
pathbuf.hash(&mut h);
|
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 */
|
* mailboxes in user configuration */
|
||||||
let fname = pathbuf
|
let fname = pathbuf
|
||||||
.strip_prefix(
|
.strip_prefix(
|
||||||
PathBuf::from(&settings.root_folder)
|
PathBuf::from(&settings.root_mailbox)
|
||||||
.expand()
|
.expand()
|
||||||
.parent()
|
.parent()
|
||||||
.unwrap_or_else(|| &Path::new("/")),
|
.unwrap_or_else(|| &Path::new("/")),
|
||||||
|
@ -213,7 +213,7 @@ impl MaildirFolder {
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
|
|
||||||
let ret = MaildirFolder {
|
let ret = MaildirMailbox {
|
||||||
hash: h.finish(),
|
hash: h.finish(),
|
||||||
name: file_name,
|
name: file_name,
|
||||||
path: fname.unwrap().to_path_buf(),
|
path: fname.unwrap().to_path_buf(),
|
||||||
|
@ -222,7 +222,7 @@ impl MaildirFolder {
|
||||||
children,
|
children,
|
||||||
usage: Arc::new(RwLock::new(SpecialUsageMailbox::Normal)),
|
usage: Arc::new(RwLock::new(SpecialUsageMailbox::Normal)),
|
||||||
is_subscribed: false,
|
is_subscribed: false,
|
||||||
permissions: FolderPermissions {
|
permissions: MailboxPermissions {
|
||||||
create_messages: !read_only,
|
create_messages: !read_only,
|
||||||
remove_messages: !read_only,
|
remove_messages: !read_only,
|
||||||
set_flags: !read_only,
|
set_flags: !read_only,
|
||||||
|
@ -250,7 +250,7 @@ impl MaildirFolder {
|
||||||
p.push(d);
|
p.push(d);
|
||||||
if !p.is_dir() {
|
if !p.is_dir() {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"{} is not a valid maildir folder",
|
"{} is not a valid maildir mailbox",
|
||||||
path.display()
|
path.display()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -260,8 +260,8 @@ impl MaildirFolder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackendFolder for MaildirFolder {
|
impl BackendMailbox for MaildirMailbox {
|
||||||
fn hash(&self) -> FolderHash {
|
fn hash(&self) -> MailboxHash {
|
||||||
self.hash
|
self.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,11 +277,11 @@ impl BackendFolder for MaildirFolder {
|
||||||
self.name = s.to_string();
|
self.name = s.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children(&self) -> &[FolderHash] {
|
fn children(&self) -> &[MailboxHash] {
|
||||||
&self.children
|
&self.children
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone(&self) -> Folder {
|
fn clone(&self) -> Mailbox {
|
||||||
Box::new(std::clone::Clone::clone(self))
|
Box::new(std::clone::Clone::clone(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,11 +289,11 @@ impl BackendFolder for MaildirFolder {
|
||||||
*self.usage.read().unwrap()
|
*self.usage.read().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parent(&self) -> Option<FolderHash> {
|
fn parent(&self) -> Option<MailboxHash> {
|
||||||
self.parent
|
self.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
fn permissions(&self) -> FolderPermissions {
|
fn permissions(&self) -> MailboxPermissions {
|
||||||
self.permissions
|
self.permissions
|
||||||
}
|
}
|
||||||
fn is_subscribed(&self) -> bool {
|
fn is_subscribed(&self) -> bool {
|
||||||
|
|
|
@ -20,10 +20,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
BackendFolder, BackendOp, Folder, FolderHash, MailBackend, RefreshEvent, RefreshEventConsumer,
|
BackendMailbox, BackendOp, MailBackend, Mailbox, MailboxHash, RefreshEvent,
|
||||||
RefreshEventKind::*,
|
RefreshEventConsumer, RefreshEventKind::*,
|
||||||
};
|
};
|
||||||
use super::{MaildirFolder, MaildirOp};
|
use super::{MaildirMailbox, MaildirOp};
|
||||||
use crate::async_workers::*;
|
use crate::async_workers::*;
|
||||||
use crate::conf::AccountSettings;
|
use crate::conf::AccountSettings;
|
||||||
use crate::email::{Envelope, EnvelopeHash, Flag};
|
use crate::email::{Envelope, EnvelopeHash, Flag};
|
||||||
|
@ -88,7 +88,7 @@ impl From<PathBuf> for MaildirPath {
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct HashIndex {
|
pub struct HashIndex {
|
||||||
index: FnvHashMap<EnvelopeHash, MaildirPath>,
|
index: FnvHashMap<EnvelopeHash, MaildirPath>,
|
||||||
hash: FolderHash,
|
hash: MailboxHash,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deref for HashIndex {
|
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
|
/// Maildir backend https://cr.yp.to/proto/maildir.html
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MaildirType {
|
pub struct MaildirType {
|
||||||
name: String,
|
name: String,
|
||||||
folders: FnvHashMap<FolderHash, MaildirFolder>,
|
mailboxes: FnvHashMap<MailboxHash, MaildirMailbox>,
|
||||||
folder_index: Arc<Mutex<FnvHashMap<EnvelopeHash, FolderHash>>>,
|
mailbox_index: Arc<Mutex<FnvHashMap<EnvelopeHash, MailboxHash>>>,
|
||||||
hash_indexes: HashIndexes,
|
hash_indexes: HashIndexes,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
}
|
}
|
||||||
|
@ -181,32 +181,32 @@ impl MailBackend for MaildirType {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
|
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.folders
|
.mailboxes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(h, f)| (*h, BackendFolder::clone(f)))
|
.map(|(h, f)| (*h, BackendMailbox::clone(f)))
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
|
||||||
self.multicore(4, folder)
|
self.multicore(4, mailbox)
|
||||||
}
|
}
|
||||||
fn refresh(
|
fn refresh(
|
||||||
&mut self,
|
&mut self,
|
||||||
folder_hash: FolderHash,
|
mailbox_hash: MailboxHash,
|
||||||
sender: RefreshEventConsumer,
|
sender: RefreshEventConsumer,
|
||||||
) -> Result<Async<()>> {
|
) -> Result<Async<()>> {
|
||||||
let w = AsyncBuilder::new();
|
let w = AsyncBuilder::new();
|
||||||
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
|
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
|
||||||
|
|
||||||
let handle = {
|
let handle = {
|
||||||
let folder: &MaildirFolder = &self.folders[&folder_hash];
|
let mailbox: &MaildirMailbox = &self.mailboxes[&mailbox_hash];
|
||||||
let path: PathBuf = folder.fs_path().into();
|
let path: PathBuf = mailbox.fs_path().into();
|
||||||
let name = format!("refresh {:?}", folder.name());
|
let name = format!("refresh {:?}", mailbox.name());
|
||||||
let root_path = self.path.to_path_buf();
|
let root_path = self.path.to_path_buf();
|
||||||
let map = self.hash_indexes.clone();
|
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| {
|
Box::new(move |work_context: crate::async_workers::WorkContext| {
|
||||||
work_context
|
work_context
|
||||||
|
@ -237,14 +237,14 @@ impl MailBackend for MaildirType {
|
||||||
}
|
}
|
||||||
let mut current_hashes = {
|
let mut current_hashes = {
|
||||||
let mut map = map.lock().unwrap();
|
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>>()
|
map.keys().cloned().collect::<FnvHashSet<EnvelopeHash>>()
|
||||||
};
|
};
|
||||||
for file in files {
|
for file in files {
|
||||||
let hash = get_file_hash(&file);
|
let hash = get_file_hash(&file);
|
||||||
{
|
{
|
||||||
let mut map = map.lock().unwrap();
|
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) {
|
if map.contains_key(&hash) {
|
||||||
map.remove(&hash);
|
map.remove(&hash);
|
||||||
current_hashes.remove(&hash);
|
current_hashes.remove(&hash);
|
||||||
|
@ -252,9 +252,9 @@ impl MailBackend for MaildirType {
|
||||||
}
|
}
|
||||||
(*map).insert(hash, PathBuf::from(&file).into());
|
(*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) {
|
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)
|
let file_name = PathBuf::from(file)
|
||||||
.strip_prefix(&root_path)
|
.strip_prefix(&root_path)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -277,7 +277,7 @@ impl MailBackend for MaildirType {
|
||||||
bincode::serialize_into(writer, &e).unwrap();
|
bincode::serialize_into(writer, &e).unwrap();
|
||||||
}
|
}
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: Create(Box::new(e)),
|
kind: Create(Box::new(e)),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -290,7 +290,7 @@ impl MailBackend for MaildirType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for ev in current_hashes.into_iter().map(|h| RefreshEvent {
|
for ev in current_hashes.into_iter().map(|h| RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: Remove(h),
|
kind: Remove(h),
|
||||||
}) {
|
}) {
|
||||||
sender.send(ev);
|
sender.send(ev);
|
||||||
|
@ -299,7 +299,7 @@ impl MailBackend for MaildirType {
|
||||||
};
|
};
|
||||||
if let Err(err) = thunk(&sender) {
|
if let Err(err) = thunk(&sender) {
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: Failure(err),
|
kind: Failure(err),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -319,14 +319,14 @@ impl MailBackend for MaildirType {
|
||||||
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
|
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
|
||||||
debug!("watching {:?}", root_path);
|
debug!("watching {:?}", root_path);
|
||||||
let hash_indexes = self.hash_indexes.clone();
|
let hash_indexes = self.hash_indexes.clone();
|
||||||
let folder_index = self.folder_index.clone();
|
let mailbox_index = self.mailbox_index.clone();
|
||||||
let folder_counts = self
|
let mailbox_counts = self
|
||||||
.folders
|
.mailboxes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(&k, v)| (k, (v.unseen.clone(), v.total.clone())))
|
.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()
|
let handle = thread::Builder::new()
|
||||||
.name("folder watch".to_string())
|
.name("mailbox watch".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
// Move `watcher` in the closure's scope so that it doesn't get dropped.
|
// Move `watcher` in the closure's scope so that it doesn't get dropped.
|
||||||
let _watcher = watcher;
|
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
|
let file_name = pathbuf
|
||||||
.as_path()
|
.as_path()
|
||||||
.strip_prefix(&root_path)
|
.strip_prefix(&root_path)
|
||||||
|
@ -368,12 +368,12 @@ impl MailBackend for MaildirType {
|
||||||
.to_path_buf();
|
.to_path_buf();
|
||||||
if let Some(env) = add_path_to_index(
|
if let Some(env) = add_path_to_index(
|
||||||
&hash_indexes,
|
&hash_indexes,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
pathbuf.as_path(),
|
pathbuf.as_path(),
|
||||||
&cache_dir,
|
&cache_dir,
|
||||||
file_name,
|
file_name,
|
||||||
) {
|
) {
|
||||||
folder_index.lock().unwrap().insert(env.hash(),folder_hash);
|
mailbox_index.lock().unwrap().insert(env.hash(),mailbox_hash);
|
||||||
debug!(
|
debug!(
|
||||||
"Create event {} {} {}",
|
"Create event {} {} {}",
|
||||||
env.hash(),
|
env.hash(),
|
||||||
|
@ -381,11 +381,11 @@ impl MailBackend for MaildirType {
|
||||||
pathbuf.display()
|
pathbuf.display()
|
||||||
);
|
);
|
||||||
if !env.is_seen() {
|
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 {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: Create(Box::new(env)),
|
kind: Create(Box::new(env)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -394,10 +394,10 @@ impl MailBackend for MaildirType {
|
||||||
DebouncedEvent::NoticeWrite(pathbuf)
|
DebouncedEvent::NoticeWrite(pathbuf)
|
||||||
| DebouncedEvent::Write(pathbuf) => {
|
| DebouncedEvent::Write(pathbuf) => {
|
||||||
debug!("DebouncedEvent::Write(path = {:?}", &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 mut hash_indexes_lock = hash_indexes.lock().unwrap();
|
||||||
let index_lock =
|
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
|
let file_name = pathbuf
|
||||||
.as_path()
|
.as_path()
|
||||||
.strip_prefix(&root_path)
|
.strip_prefix(&root_path)
|
||||||
|
@ -418,14 +418,14 @@ impl MailBackend for MaildirType {
|
||||||
* envelope. */
|
* envelope. */
|
||||||
if let Some(env) = add_path_to_index(
|
if let Some(env) = add_path_to_index(
|
||||||
&hash_indexes,
|
&hash_indexes,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
pathbuf.as_path(),
|
pathbuf.as_path(),
|
||||||
&cache_dir,
|
&cache_dir,
|
||||||
file_name,
|
file_name,
|
||||||
) {
|
) {
|
||||||
folder_index.lock().unwrap().insert(env.hash(),folder_hash);
|
mailbox_index.lock().unwrap().insert(env.hash(),mailbox_hash);
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: Create(Box::new(env)),
|
kind: Create(Box::new(env)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -438,7 +438,7 @@ impl MailBackend for MaildirType {
|
||||||
let op = Box::new(MaildirOp::new(
|
let op = Box::new(MaildirOp::new(
|
||||||
new_hash,
|
new_hash,
|
||||||
hash_indexes.clone(),
|
hash_indexes.clone(),
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
));
|
));
|
||||||
if let Some(env) = Envelope::from_token(op, new_hash) {
|
if let Some(env) = Envelope::from_token(op, new_hash) {
|
||||||
debug!("{}\t{:?}", new_hash, &pathbuf);
|
debug!("{}\t{:?}", new_hash, &pathbuf);
|
||||||
|
@ -451,7 +451,7 @@ impl MailBackend for MaildirType {
|
||||||
/* Send Write notice */
|
/* Send Write notice */
|
||||||
|
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: Update(old_hash, Box::new(env)),
|
kind: Update(old_hash, Box::new(env)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -461,9 +461,9 @@ impl MailBackend for MaildirType {
|
||||||
DebouncedEvent::NoticeRemove(pathbuf)
|
DebouncedEvent::NoticeRemove(pathbuf)
|
||||||
| DebouncedEvent::Remove(pathbuf) => {
|
| DebouncedEvent::Remove(pathbuf) => {
|
||||||
debug!("DebouncedEvent::Remove(path = {:?}", 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 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, _)) =
|
let hash: EnvelopeHash = if let Some((k, _)) =
|
||||||
index_lock.iter().find(|(_, v)| *v.buf == pathbuf)
|
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
|
//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 {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: Remove(hash),
|
kind: Remove(hash),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -505,12 +505,12 @@ impl MailBackend for MaildirType {
|
||||||
"DebouncedEvent::Rename(src = {:?}, dest = {:?})",
|
"DebouncedEvent::Rename(src = {:?}, dest = {:?})",
|
||||||
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 old_hash: EnvelopeHash = get_file_hash(src.as_path());
|
||||||
let new_hash: EnvelopeHash = get_file_hash(dest.as_path());
|
let new_hash: EnvelopeHash = get_file_hash(dest.as_path());
|
||||||
|
|
||||||
let mut hash_indexes_lock = hash_indexes.lock().unwrap();
|
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)
|
if index_lock.contains_key(&old_hash)
|
||||||
&& !index_lock[&old_hash].removed
|
&& !index_lock[&old_hash].removed
|
||||||
|
@ -524,7 +524,7 @@ impl MailBackend for MaildirType {
|
||||||
hash: get_path_hash!(dest),
|
hash: get_path_hash!(dest),
|
||||||
kind: Rename(old_hash, new_hash),
|
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());
|
index_lock.insert(new_hash, dest.into());
|
||||||
continue;
|
continue;
|
||||||
} else if !index_lock.contains_key(&new_hash)
|
} else if !index_lock.contains_key(&new_hash)
|
||||||
|
@ -556,12 +556,12 @@ impl MailBackend for MaildirType {
|
||||||
drop(hash_indexes_lock);
|
drop(hash_indexes_lock);
|
||||||
if let Some(env) = add_path_to_index(
|
if let Some(env) = add_path_to_index(
|
||||||
&hash_indexes,
|
&hash_indexes,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
dest.as_path(),
|
dest.as_path(),
|
||||||
&cache_dir,
|
&cache_dir,
|
||||||
file_name,
|
file_name,
|
||||||
) {
|
) {
|
||||||
folder_index.lock().unwrap().insert(env.hash(),folder_hash);
|
mailbox_index.lock().unwrap().insert(env.hash(), mailbox_hash);
|
||||||
debug!(
|
debug!(
|
||||||
"Create event {} {} {}",
|
"Create event {} {} {}",
|
||||||
env.hash(),
|
env.hash(),
|
||||||
|
@ -569,11 +569,11 @@ impl MailBackend for MaildirType {
|
||||||
dest.display()
|
dest.display()
|
||||||
);
|
);
|
||||||
if !env.is_seen() {
|
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 {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: Create(Box::new(env)),
|
kind: Create(Box::new(env)),
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
|
@ -595,10 +595,10 @@ impl MailBackend for MaildirType {
|
||||||
});
|
});
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
/* Trigger rescan of folder */
|
/* Trigger rescan of mailbox */
|
||||||
DebouncedEvent::Rescan => {
|
DebouncedEvent::Rescan => {
|
||||||
/* Actually should rescan all folders */
|
/* Actually should rescan all mailboxes */
|
||||||
unreachable!("Unimplemented: rescan of all folders in MaildirType")
|
unreachable!("Unimplemented: rescan of all mailboxes in MaildirType")
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
|
@ -613,20 +613,20 @@ impl MailBackend for MaildirType {
|
||||||
Box::new(MaildirOp::new(
|
Box::new(MaildirOp::new(
|
||||||
hash,
|
hash,
|
||||||
self.hash_indexes.clone(),
|
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<()> {
|
fn save(&self, bytes: &[u8], mailbox: &str, flags: Option<Flag>) -> Result<()> {
|
||||||
for f in self.folders.values() {
|
for f in self.mailboxes.values() {
|
||||||
if f.name == folder || f.path.to_str().unwrap() == folder {
|
if f.name == mailbox || f.path.to_str().unwrap() == mailbox {
|
||||||
return MaildirType::save_to_folder(f.fs_path.clone(), bytes, flags);
|
return MaildirType::save_to_mailbox(f.fs_path.clone(), bytes, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(MeliError::new(format!(
|
Err(MeliError::new(format!(
|
||||||
"'{}' is not a valid folder.",
|
"'{}' is not a valid mailbox.",
|
||||||
folder
|
mailbox
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -634,35 +634,35 @@ impl MailBackend for MaildirType {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_folder(
|
fn create_mailbox(
|
||||||
&mut self,
|
&mut self,
|
||||||
new_path: String,
|
new_path: String,
|
||||||
) -> Result<(FolderHash, FnvHashMap<FolderHash, Folder>)> {
|
) -> Result<(MailboxHash, FnvHashMap<MailboxHash, Mailbox>)> {
|
||||||
let mut path = self.path.clone();
|
let mut path = self.path.clone();
|
||||||
path.push(&new_path);
|
path.push(&new_path);
|
||||||
if !path.starts_with(&self.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)?;
|
std::fs::create_dir(&path)?;
|
||||||
/* create_dir does not create intermediate directories (like `mkdir -p`), so the parent must be a valid
|
/* 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| {
|
let parent = path.parent().and_then(|p| {
|
||||||
self.folders
|
self.mailboxes
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(_, f)| f.fs_path == p)
|
.find(|(_, f)| f.fs_path == p)
|
||||||
.map(|item| *item.0)
|
.map(|item| *item.0)
|
||||||
});
|
});
|
||||||
|
|
||||||
let folder_hash = get_path_hash!(&path);
|
let mailbox_hash = get_path_hash!(&path);
|
||||||
if let Some(parent) = parent {
|
if let Some(parent) = parent {
|
||||||
self.folders
|
self.mailboxes
|
||||||
.entry(parent)
|
.entry(parent)
|
||||||
.and_modify(|entry| entry.children.push(folder_hash));
|
.and_modify(|entry| entry.children.push(mailbox_hash));
|
||||||
}
|
}
|
||||||
let new_folder = MaildirFolder {
|
let new_mailbox = MaildirMailbox {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
path: PathBuf::from(&new_path),
|
path: PathBuf::from(&new_path),
|
||||||
name: new_path,
|
name: new_path,
|
||||||
fs_path: path,
|
fs_path: path,
|
||||||
|
@ -675,29 +675,29 @@ impl MailBackend for MaildirType {
|
||||||
total: Default::default(),
|
total: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.folders.insert(folder_hash, new_folder);
|
self.mailboxes.insert(mailbox_hash, new_mailbox);
|
||||||
Ok((folder_hash, self.folders()?))
|
Ok((mailbox_hash, self.mailboxes()?))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_folder(
|
fn delete_mailbox(
|
||||||
&mut self,
|
&mut self,
|
||||||
_folder_hash: FolderHash,
|
_mailbox_hash: MailboxHash,
|
||||||
) -> Result<FnvHashMap<FolderHash, Folder>> {
|
) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||||
Err(MeliError::new("Unimplemented."))
|
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."))
|
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."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_folder_permissions(
|
fn set_mailbox_permissions(
|
||||||
&mut self,
|
&mut self,
|
||||||
_folder_hash: FolderHash,
|
_mailbox_hash: MailboxHash,
|
||||||
_val: crate::backends::FolderPermissions,
|
_val: crate::backends::MailboxPermissions,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
Err(MeliError::new("Unimplemented."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
}
|
}
|
||||||
|
@ -708,12 +708,12 @@ impl MaildirType {
|
||||||
settings: &AccountSettings,
|
settings: &AccountSettings,
|
||||||
is_subscribed: Box<dyn Fn(&str) -> bool>,
|
is_subscribed: Box<dyn Fn(&str) -> bool>,
|
||||||
) -> Result<Box<dyn MailBackend>> {
|
) -> Result<Box<dyn MailBackend>> {
|
||||||
let mut folders: FnvHashMap<FolderHash, MaildirFolder> = Default::default();
|
let mut mailboxes: FnvHashMap<MailboxHash, MaildirMailbox> = Default::default();
|
||||||
fn recurse_folders<P: AsRef<Path>>(
|
fn recurse_mailboxes<P: AsRef<Path>>(
|
||||||
folders: &mut FnvHashMap<FolderHash, MaildirFolder>,
|
mailboxes: &mut FnvHashMap<MailboxHash, MaildirMailbox>,
|
||||||
settings: &AccountSettings,
|
settings: &AccountSettings,
|
||||||
p: P,
|
p: P,
|
||||||
) -> Result<Vec<FolderHash>> {
|
) -> Result<Vec<MailboxHash>> {
|
||||||
if !p.as_ref().exists() || !p.as_ref().is_dir() {
|
if !p.as_ref().exists() || !p.as_ref().is_dir() {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"Configuration error: Path \"{}\" {}",
|
"Configuration error: Path \"{}\" {}",
|
||||||
|
@ -734,20 +734,20 @@ impl MaildirType {
|
||||||
continue 'entries;
|
continue 'entries;
|
||||||
}
|
}
|
||||||
if path.is_dir() {
|
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.to_str().unwrap().to_string(),
|
||||||
path.file_name().unwrap().to_str().unwrap().to_string(),
|
path.file_name().unwrap().to_str().unwrap().to_string(),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
&settings,
|
&settings,
|
||||||
) {
|
) {
|
||||||
f.children = recurse_folders(folders, settings, &path)?;
|
f.children = recurse_mailboxes(mailboxes, settings, &path)?;
|
||||||
f.children
|
f.children
|
||||||
.iter()
|
.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();
|
.count();
|
||||||
children.push(f.hash);
|
children.push(f.hash);
|
||||||
folders.insert(f.hash, f);
|
mailboxes.insert(f.hash, f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -755,55 +755,55 @@ impl MaildirType {
|
||||||
}
|
}
|
||||||
Ok(children)
|
Ok(children)
|
||||||
};
|
};
|
||||||
let root_path = PathBuf::from(settings.root_folder()).expand();
|
let root_path = PathBuf::from(settings.root_mailbox()).expand();
|
||||||
if !root_path.exists() {
|
if !root_path.exists() {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"Configuration error ({}): root_path `{}` is not a valid directory.",
|
"Configuration error ({}): root_path `{}` is not a valid directory.",
|
||||||
settings.name(),
|
settings.name(),
|
||||||
settings.root_folder.as_str()
|
settings.root_mailbox.as_str()
|
||||||
)));
|
)));
|
||||||
} else if !root_path.is_dir() {
|
} else if !root_path.is_dir() {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"Configuration error ({}): root_path `{}` is not a directory.",
|
"Configuration error ({}): root_path `{}` is not a directory.",
|
||||||
settings.name(),
|
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.to_str().unwrap().to_string(),
|
||||||
root_path.file_name().unwrap().to_str().unwrap().to_string(),
|
root_path.file_name().unwrap().to_str().unwrap().to_string(),
|
||||||
None,
|
None,
|
||||||
Vec::with_capacity(0),
|
Vec::with_capacity(0),
|
||||||
settings,
|
settings,
|
||||||
) {
|
) {
|
||||||
folders.insert(f.hash, f);
|
mailboxes.insert(f.hash, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if folders.is_empty() {
|
if mailboxes.is_empty() {
|
||||||
let children = recurse_folders(&mut folders, settings, &root_path)?;
|
let children = recurse_mailboxes(&mut mailboxes, settings, &root_path)?;
|
||||||
children
|
children
|
||||||
.iter()
|
.iter()
|
||||||
.map(|c| folders.get_mut(c).map(|f| f.parent = None))
|
.map(|c| mailboxes.get_mut(c).map(|f| f.parent = None))
|
||||||
.count();
|
.count();
|
||||||
} else {
|
} else {
|
||||||
let root_hash = *folders.keys().nth(0).unwrap();
|
let root_hash = *mailboxes.keys().nth(0).unwrap();
|
||||||
let children = recurse_folders(&mut folders, settings, &root_path)?;
|
let children = recurse_mailboxes(&mut mailboxes, settings, &root_path)?;
|
||||||
children
|
children
|
||||||
.iter()
|
.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();
|
.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()) {
|
if is_subscribed(f.path()) {
|
||||||
f.is_subscribed = true;
|
f.is_subscribed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut hash_indexes =
|
let mut hash_indexes =
|
||||||
FnvHashMap::with_capacity_and_hasher(folders.len(), Default::default());
|
FnvHashMap::with_capacity_and_hasher(mailboxes.len(), Default::default());
|
||||||
for &fh in folders.keys() {
|
for &fh in mailboxes.keys() {
|
||||||
hash_indexes.insert(
|
hash_indexes.insert(
|
||||||
fh,
|
fh,
|
||||||
HashIndex {
|
HashIndex {
|
||||||
|
@ -814,37 +814,37 @@ impl MaildirType {
|
||||||
}
|
}
|
||||||
Ok(Box::new(MaildirType {
|
Ok(Box::new(MaildirType {
|
||||||
name: settings.name().to_string(),
|
name: settings.name().to_string(),
|
||||||
folders,
|
mailboxes,
|
||||||
hash_indexes: Arc::new(Mutex::new(hash_indexes)),
|
hash_indexes: Arc::new(Mutex::new(hash_indexes)),
|
||||||
folder_index: Default::default(),
|
mailbox_index: Default::default(),
|
||||||
path: root_path,
|
path: root_path,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
fn owned_folder_idx(&self, folder: &Folder) -> FolderHash {
|
fn owned_mailbox_idx(&self, mailbox: &Mailbox) -> MailboxHash {
|
||||||
*self
|
*self
|
||||||
.folders
|
.mailboxes
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(_, f)| f.hash() == folder.hash())
|
.find(|(_, f)| f.hash() == mailbox.hash())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.0
|
.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 mut w = AsyncBuilder::new();
|
||||||
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
|
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
|
||||||
|
|
||||||
let handle = {
|
let handle = {
|
||||||
let tx = w.tx();
|
let tx = w.tx();
|
||||||
let folder: &MaildirFolder = &self.folders[&self.owned_folder_idx(folder)];
|
let mailbox: &MaildirMailbox = &self.mailboxes[&self.owned_mailbox_idx(mailbox)];
|
||||||
let folder_hash = folder.hash();
|
let mailbox_hash = mailbox.hash();
|
||||||
let unseen = folder.unseen.clone();
|
let unseen = mailbox.unseen.clone();
|
||||||
let total = folder.total.clone();
|
let total = mailbox.total.clone();
|
||||||
let tx_final = w.tx();
|
let tx_final = w.tx();
|
||||||
let mut path: PathBuf = folder.fs_path().into();
|
let mut path: PathBuf = mailbox.fs_path().into();
|
||||||
let name = format!("parsing {:?}", folder.name());
|
let name = format!("parsing {:?}", mailbox.name());
|
||||||
let root_path = self.path.to_path_buf();
|
let root_path = self.path.to_path_buf();
|
||||||
let map = self.hash_indexes.clone();
|
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| {
|
let closure = move |work_context: crate::async_workers::WorkContext| {
|
||||||
work_context
|
work_context
|
||||||
|
@ -886,7 +886,7 @@ impl MaildirType {
|
||||||
let cache_dir = cache_dir.clone();
|
let cache_dir = cache_dir.clone();
|
||||||
let tx = tx.clone();
|
let tx = tx.clone();
|
||||||
let map = map.clone();
|
let map = map.clone();
|
||||||
let folder_index = folder_index.clone();
|
let mailbox_index = mailbox_index.clone();
|
||||||
let root_path = root_path.clone();
|
let root_path = root_path.clone();
|
||||||
let s = scope.builder().name(name.clone()).spawn(move |_| {
|
let s = scope.builder().name(name.clone()).spawn(move |_| {
|
||||||
let len = chunk.len();
|
let len = chunk.len();
|
||||||
|
@ -896,7 +896,7 @@ impl MaildirType {
|
||||||
for c in chunk.chunks(size) {
|
for c in chunk.chunks(size) {
|
||||||
//thread::yield_now();
|
//thread::yield_now();
|
||||||
let map = map.clone();
|
let map = map.clone();
|
||||||
let folder_index = folder_index.clone();
|
let mailbox_index = mailbox_index.clone();
|
||||||
let len = c.len();
|
let len = c.len();
|
||||||
for file in c {
|
for file in c {
|
||||||
/* Check if we have a cache file with this email's
|
/* Check if we have a cache file with this email's
|
||||||
|
@ -916,13 +916,13 @@ impl MaildirType {
|
||||||
bincode::deserialize_from(reader);
|
bincode::deserialize_from(reader);
|
||||||
if let Ok(env) = result {
|
if let Ok(env) = result {
|
||||||
let mut map = map.lock().unwrap();
|
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();
|
let hash = env.hash();
|
||||||
map.insert(hash, file.clone().into());
|
map.insert(hash, file.clone().into());
|
||||||
folder_index
|
mailbox_index
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(hash, folder_hash);
|
.insert(hash, mailbox_hash);
|
||||||
if !env.is_seen() {
|
if !env.is_seen() {
|
||||||
*unseen.lock().unwrap() += 1;
|
*unseen.lock().unwrap() += 1;
|
||||||
}
|
}
|
||||||
|
@ -934,19 +934,19 @@ impl MaildirType {
|
||||||
let hash = get_file_hash(file);
|
let hash = get_file_hash(file);
|
||||||
{
|
{
|
||||||
let mut map = map.lock().unwrap();
|
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());
|
(*map).insert(hash, PathBuf::from(file).into());
|
||||||
}
|
}
|
||||||
let op = Box::new(MaildirOp::new(
|
let op = Box::new(MaildirOp::new(
|
||||||
hash,
|
hash,
|
||||||
map.clone(),
|
map.clone(),
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
));
|
));
|
||||||
if let Some(e) = Envelope::from_token(op, hash) {
|
if let Some(e) = Envelope::from_token(op, hash) {
|
||||||
folder_index
|
mailbox_index
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(e.hash(), folder_hash);
|
.insert(e.hash(), mailbox_hash);
|
||||||
if let Ok(cached) =
|
if let Ok(cached) =
|
||||||
cache_dir.place_cache_file(file_name)
|
cache_dir.place_cache_file(file_name)
|
||||||
{
|
{
|
||||||
|
@ -1011,7 +1011,7 @@ impl MaildirType {
|
||||||
w.build(handle)
|
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");
|
path.push("cur");
|
||||||
{
|
{
|
||||||
let mut rand_buf = [0u8; 16];
|
let mut rand_buf = [0u8; 16];
|
||||||
|
@ -1071,18 +1071,18 @@ impl MaildirType {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_config(s: &AccountSettings) -> Result<()> {
|
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() {
|
if !root_path.exists() {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"Configuration error ({}): root_path `{}` is not a valid directory.",
|
"Configuration error ({}): root_path `{}` is not a valid directory.",
|
||||||
s.name(),
|
s.name(),
|
||||||
s.root_folder.as_str()
|
s.root_mailbox.as_str()
|
||||||
)));
|
)));
|
||||||
} else if !root_path.is_dir() {
|
} else if !root_path.is_dir() {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"Configuration error ({}): root_path `{}` is not a directory.",
|
"Configuration error ({}): root_path `{}` is not a directory.",
|
||||||
s.name(),
|
s.name(),
|
||||||
s.root_folder.as_str()
|
s.root_mailbox.as_str()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1092,7 +1092,7 @@ impl MaildirType {
|
||||||
|
|
||||||
fn add_path_to_index(
|
fn add_path_to_index(
|
||||||
hash_index: &HashIndexes,
|
hash_index: &HashIndexes,
|
||||||
folder_hash: FolderHash,
|
mailbox_hash: MailboxHash,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
cache_dir: &xdg::BaseDirectories,
|
cache_dir: &xdg::BaseDirectories,
|
||||||
file_name: PathBuf,
|
file_name: PathBuf,
|
||||||
|
@ -1102,16 +1102,16 @@ fn add_path_to_index(
|
||||||
let hash = get_file_hash(path);
|
let hash = get_file_hash(path);
|
||||||
{
|
{
|
||||||
let mut map = hash_index.lock().unwrap();
|
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());
|
map.insert(hash, path.to_path_buf().into());
|
||||||
debug!(
|
debug!(
|
||||||
"inserted {} in {} map, len={}",
|
"inserted {} in {} map, len={}",
|
||||||
hash,
|
hash,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
map.len()
|
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) {
|
if let Some(e) = Envelope::from_token(op, hash) {
|
||||||
debug!("add_path_to_index gen {}\t{}", hash, file_name.display());
|
debug!("add_path_to_index gen {}\t{}", hash, file_name.display());
|
||||||
if let Ok(cached) = cache_dir.place_cache_file(file_name) {
|
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::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
||||||
use crate::backends::BackendOp;
|
use crate::backends::BackendOp;
|
||||||
use crate::backends::FolderHash;
|
use crate::backends::MailboxHash;
|
||||||
use crate::backends::{
|
use crate::backends::{
|
||||||
BackendFolder, Folder, FolderPermissions, MailBackend, RefreshEvent, RefreshEventConsumer,
|
BackendMailbox, MailBackend, Mailbox, MailboxPermissions, RefreshEvent, RefreshEventConsumer,
|
||||||
RefreshEventKind, SpecialUsageMailbox,
|
RefreshEventKind, SpecialUsageMailbox,
|
||||||
};
|
};
|
||||||
use crate::conf::AccountSettings;
|
use crate::conf::AccountSettings;
|
||||||
|
@ -74,22 +74,22 @@ fn get_rw_lock_blocking(f: &File) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct MboxFolder {
|
struct MboxMailbox {
|
||||||
hash: FolderHash,
|
hash: MailboxHash,
|
||||||
name: String,
|
name: String,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
content: Vec<u8>,
|
content: Vec<u8>,
|
||||||
children: Vec<FolderHash>,
|
children: Vec<MailboxHash>,
|
||||||
parent: Option<FolderHash>,
|
parent: Option<MailboxHash>,
|
||||||
usage: Arc<RwLock<SpecialUsageMailbox>>,
|
usage: Arc<RwLock<SpecialUsageMailbox>>,
|
||||||
is_subscribed: bool,
|
is_subscribed: bool,
|
||||||
permissions: FolderPermissions,
|
permissions: MailboxPermissions,
|
||||||
pub total: Arc<Mutex<usize>>,
|
pub total: Arc<Mutex<usize>>,
|
||||||
pub unseen: Arc<Mutex<usize>>,
|
pub unseen: Arc<Mutex<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackendFolder for MboxFolder {
|
impl BackendMailbox for MboxMailbox {
|
||||||
fn hash(&self) -> FolderHash {
|
fn hash(&self) -> MailboxHash {
|
||||||
self.hash
|
self.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,8 +106,8 @@ impl BackendFolder for MboxFolder {
|
||||||
self.name = s.to_string();
|
self.name = s.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone(&self) -> Folder {
|
fn clone(&self) -> Mailbox {
|
||||||
Box::new(MboxFolder {
|
Box::new(MboxMailbox {
|
||||||
hash: self.hash,
|
hash: self.hash,
|
||||||
name: self.name.clone(),
|
name: self.name.clone(),
|
||||||
path: self.path.clone(),
|
path: self.path.clone(),
|
||||||
|
@ -122,11 +122,11 @@ impl BackendFolder for MboxFolder {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children(&self) -> &[FolderHash] {
|
fn children(&self) -> &[MailboxHash] {
|
||||||
&self.children
|
&self.children
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parent(&self) -> Option<FolderHash> {
|
fn parent(&self) -> Option<MailboxHash> {
|
||||||
self.parent
|
self.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ impl BackendFolder for MboxFolder {
|
||||||
*self.usage.read().unwrap()
|
*self.usage.read().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn permissions(&self) -> FolderPermissions {
|
fn permissions(&self) -> MailboxPermissions {
|
||||||
self.permissions
|
self.permissions
|
||||||
}
|
}
|
||||||
fn is_subscribed(&self) -> bool {
|
fn is_subscribed(&self) -> bool {
|
||||||
|
@ -388,28 +388,28 @@ pub fn mbox_parse(
|
||||||
pub struct MboxType {
|
pub struct MboxType {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
index: Arc<Mutex<FnvHashMap<EnvelopeHash, (Offset, Length)>>>,
|
index: Arc<Mutex<FnvHashMap<EnvelopeHash, (Offset, Length)>>>,
|
||||||
folders: Arc<Mutex<FnvHashMap<FolderHash, MboxFolder>>>,
|
mailboxes: Arc<Mutex<FnvHashMap<MailboxHash, MboxMailbox>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MailBackend for MboxType {
|
impl MailBackend for MboxType {
|
||||||
fn is_online(&self) -> Result<()> {
|
fn is_online(&self) -> Result<()> {
|
||||||
Ok(())
|
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 mut w = AsyncBuilder::new();
|
||||||
let handle = {
|
let handle = {
|
||||||
let tx = w.tx();
|
let tx = w.tx();
|
||||||
let index = self.index.clone();
|
let index = self.index.clone();
|
||||||
let folder_path = folder.path().to_string();
|
let mailbox_path = mailbox.path().to_string();
|
||||||
let folder_hash = folder.hash();
|
let mailbox_hash = mailbox.hash();
|
||||||
let folders = self.folders.clone();
|
let mailboxes = self.mailboxes.clone();
|
||||||
let closure = move |_work_context| {
|
let closure = move |_work_context| {
|
||||||
let tx = tx.clone();
|
let tx = tx.clone();
|
||||||
let index = index.clone();
|
let index = index.clone();
|
||||||
let file = match std::fs::OpenOptions::new()
|
let file = match std::fs::OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.open(&folder_path)
|
.open(&mailbox_path)
|
||||||
{
|
{
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -431,9 +431,9 @@ impl MailBackend for MboxType {
|
||||||
.to_full_result()
|
.to_full_result()
|
||||||
.map_err(|e| MeliError::from(e));
|
.map_err(|e| MeliError::from(e));
|
||||||
{
|
{
|
||||||
let mut folder_lock = folders.lock().unwrap();
|
let mut mailbox_lock = mailboxes.lock().unwrap();
|
||||||
folder_lock
|
mailbox_lock
|
||||||
.entry(folder_hash)
|
.entry(mailbox_hash)
|
||||||
.and_modify(|f| f.content = contents);
|
.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))
|
let mut watcher = watcher(tx, std::time::Duration::from_secs(10))
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
.map_err(MeliError::new)?;
|
.map_err(MeliError::new)?;
|
||||||
for f in self.folders.lock().unwrap().values() {
|
for f in self.mailboxes.lock().unwrap().values() {
|
||||||
watcher
|
watcher
|
||||||
.watch(&f.path, RecursiveMode::Recursive)
|
.watch(&f.path, RecursiveMode::Recursive)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
|
@ -462,7 +462,7 @@ impl MailBackend for MboxType {
|
||||||
debug!("watching {:?}", f.path.as_path());
|
debug!("watching {:?}", f.path.as_path());
|
||||||
}
|
}
|
||||||
let index = self.index.clone();
|
let index = self.index.clone();
|
||||||
let folders = self.folders.clone();
|
let mailboxes = self.mailboxes.clone();
|
||||||
let handle = std::thread::Builder::new()
|
let handle = std::thread::Builder::new()
|
||||||
.name(format!(
|
.name(format!(
|
||||||
"watching {}",
|
"watching {}",
|
||||||
|
@ -473,7 +473,7 @@ impl MailBackend for MboxType {
|
||||||
let _watcher = watcher;
|
let _watcher = watcher;
|
||||||
let _work_context = work_context;
|
let _work_context = work_context;
|
||||||
let index = index;
|
let index = index;
|
||||||
let folders = folders;
|
let mailboxes = mailboxes;
|
||||||
loop {
|
loop {
|
||||||
match rx.recv() {
|
match rx.recv() {
|
||||||
/*
|
/*
|
||||||
|
@ -490,7 +490,7 @@ impl MailBackend for MboxType {
|
||||||
/* Update */
|
/* Update */
|
||||||
DebouncedEvent::NoticeWrite(pathbuf)
|
DebouncedEvent::NoticeWrite(pathbuf)
|
||||||
| DebouncedEvent::Write(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()
|
let file = match std::fs::OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
|
@ -502,7 +502,7 @@ impl MailBackend for MboxType {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
get_rw_lock_blocking(&file);
|
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 buf_reader = BufReader::new(file);
|
||||||
let mut contents = Vec::new();
|
let mut contents = Vec::new();
|
||||||
if let Err(e) = buf_reader.read_to_end(&mut contents) {
|
if let Err(e) = buf_reader.read_to_end(&mut contents) {
|
||||||
|
@ -510,46 +510,46 @@ impl MailBackend for MboxType {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if contents
|
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(
|
if let Ok(envelopes) = mbox_parse(
|
||||||
index.clone(),
|
index.clone(),
|
||||||
&contents[folder_lock[&folder_hash].content.len()..],
|
&contents[mailbox_lock[&mailbox_hash].content.len()..],
|
||||||
folder_lock[&folder_hash].content.len(),
|
mailbox_lock[&mailbox_hash].content.len(),
|
||||||
)
|
)
|
||||||
.to_full_result()
|
.to_full_result()
|
||||||
{
|
{
|
||||||
for env in envelopes {
|
for env in envelopes {
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: RefreshEventKind::Create(Box::new(env)),
|
kind: RefreshEventKind::Create(Box::new(env)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: RefreshEventKind::Rescan,
|
kind: RefreshEventKind::Rescan,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
folder_lock
|
mailbox_lock
|
||||||
.entry(folder_hash)
|
.entry(mailbox_hash)
|
||||||
.and_modify(|f| f.content = contents);
|
.and_modify(|f| f.content = contents);
|
||||||
}
|
}
|
||||||
/* Remove */
|
/* Remove */
|
||||||
DebouncedEvent::NoticeRemove(pathbuf)
|
DebouncedEvent::NoticeRemove(pathbuf)
|
||||||
| DebouncedEvent::Remove(pathbuf) => {
|
| DebouncedEvent::Remove(pathbuf) => {
|
||||||
if folders
|
if mailboxes
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.values()
|
.values()
|
||||||
.any(|f| &f.path == &pathbuf)
|
.any(|f| &f.path == &pathbuf)
|
||||||
{
|
{
|
||||||
let folder_hash = get_path_hash!(&pathbuf);
|
let mailbox_hash = get_path_hash!(&pathbuf);
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
||||||
"mbox folder {} was removed.",
|
"mbox mailbox {} was removed.",
|
||||||
pathbuf.display()
|
pathbuf.display()
|
||||||
))),
|
))),
|
||||||
});
|
});
|
||||||
|
@ -557,12 +557,12 @@ impl MailBackend for MboxType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DebouncedEvent::Rename(src, dest) => {
|
DebouncedEvent::Rename(src, dest) => {
|
||||||
if folders.lock().unwrap().values().any(|f| &f.path == &src) {
|
if mailboxes.lock().unwrap().values().any(|f| &f.path == &src) {
|
||||||
let folder_hash = get_path_hash!(&src);
|
let mailbox_hash = get_path_hash!(&src);
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: folder_hash,
|
hash: mailbox_hash,
|
||||||
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
kind: RefreshEventKind::Failure(MeliError::new(format!(
|
||||||
"mbox folder {} was renamed to {}.",
|
"mbox mailbox {} was renamed to {}.",
|
||||||
src.display(),
|
src.display(),
|
||||||
dest.display()
|
dest.display()
|
||||||
))),
|
))),
|
||||||
|
@ -570,9 +570,9 @@ impl MailBackend for MboxType {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Trigger rescan of folders */
|
/* Trigger rescan of mailboxes */
|
||||||
DebouncedEvent::Rescan => {
|
DebouncedEvent::Rescan => {
|
||||||
for h in folders.lock().unwrap().keys() {
|
for h in mailboxes.lock().unwrap().keys() {
|
||||||
sender.send(RefreshEvent {
|
sender.send(RefreshEvent {
|
||||||
hash: *h,
|
hash: *h,
|
||||||
kind: RefreshEventKind::Rescan,
|
kind: RefreshEventKind::Rescan,
|
||||||
|
@ -588,13 +588,13 @@ impl MailBackend for MboxType {
|
||||||
})?;
|
})?;
|
||||||
Ok(handle.thread().id())
|
Ok(handle.thread().id())
|
||||||
}
|
}
|
||||||
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
|
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.folders
|
.mailboxes
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(h, f)| (*h, f.clone() as Folder))
|
.map(|(h, f)| (*h, f.clone() as Mailbox))
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp> {
|
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))
|
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."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,11 +619,11 @@ impl MboxType {
|
||||||
s: &AccountSettings,
|
s: &AccountSettings,
|
||||||
_is_subscribed: Box<dyn Fn(&str) -> bool>,
|
_is_subscribed: Box<dyn Fn(&str) -> bool>,
|
||||||
) -> Result<Box<dyn MailBackend>> {
|
) -> 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() {
|
if !path.exists() {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"\"root_folder\" {} for account {} is not a valid path.",
|
"\"root_mailbox\" {} for account {} is not a valid path.",
|
||||||
s.root_folder.as_str(),
|
s.root_mailbox.as_str(),
|
||||||
s.name()
|
s.name()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -644,9 +644,9 @@ impl MboxType {
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
|
|
||||||
ret.folders.lock().unwrap().insert(
|
ret.mailboxes.lock().unwrap().insert(
|
||||||
hash,
|
hash,
|
||||||
MboxFolder {
|
MboxMailbox {
|
||||||
hash,
|
hash,
|
||||||
path: ret.path.clone(),
|
path: ret.path.clone(),
|
||||||
name,
|
name,
|
||||||
|
@ -655,7 +655,7 @@ impl MboxType {
|
||||||
parent: None,
|
parent: None,
|
||||||
usage: Arc::new(RwLock::new(SpecialUsageMailbox::Normal)),
|
usage: Arc::new(RwLock::new(SpecialUsageMailbox::Normal)),
|
||||||
is_subscribed: true,
|
is_subscribed: true,
|
||||||
permissions: FolderPermissions {
|
permissions: MailboxPermissions {
|
||||||
create_messages: !read_only,
|
create_messages: !read_only,
|
||||||
remove_messages: !read_only,
|
remove_messages: !read_only,
|
||||||
set_flags: !read_only,
|
set_flags: !read_only,
|
||||||
|
@ -671,8 +671,8 @@ impl MboxType {
|
||||||
);
|
);
|
||||||
/*
|
/*
|
||||||
/* Look for other mailboxes */
|
/* Look for other mailboxes */
|
||||||
let parent_folder = Path::new(path).parent().unwrap();
|
let parent_mailbox = Path::new(path).parent().unwrap();
|
||||||
let read_dir = std::fs::read_dir(parent_folder);
|
let read_dir = std::fs::read_dir(parent_mailbox);
|
||||||
if read_dir.is_ok() {
|
if read_dir.is_ok() {
|
||||||
for f in read_dir.unwrap() {
|
for f in read_dir.unwrap() {
|
||||||
if f.is_err() {
|
if f.is_err() {
|
||||||
|
@ -685,9 +685,9 @@ impl MboxType {
|
||||||
.map(|f| f.to_string_lossy().into())
|
.map(|f| f.to_string_lossy().into())
|
||||||
.unwrap_or(String::new());
|
.unwrap_or(String::new());
|
||||||
let hash = get_path_hash!(f);
|
let hash = get_path_hash!(f);
|
||||||
ret.folders.lock().unwrap().insert(
|
ret.mailboxes.lock().unwrap().insert(
|
||||||
hash,
|
hash,
|
||||||
MboxFolder {
|
MboxMailbox {
|
||||||
hash,
|
hash,
|
||||||
path: f,
|
path: f,
|
||||||
name,
|
name,
|
||||||
|
@ -704,11 +704,11 @@ impl MboxType {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_config(s: &AccountSettings) -> Result<()> {
|
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() {
|
if !path.exists() {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"\"root_folder\" {} for account {} is not a valid path.",
|
"\"root_mailbox\" {} for account {} is not a valid path.",
|
||||||
s.root_folder.as_str(),
|
s.root_mailbox.as_str(),
|
||||||
s.name()
|
s.name()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
||||||
use crate::backends::FolderHash;
|
use crate::backends::MailboxHash;
|
||||||
use crate::backends::{
|
use crate::backends::{
|
||||||
BackendFolder, BackendOp, Folder, FolderPermissions, MailBackend, RefreshEventConsumer,
|
BackendMailbox, BackendOp, MailBackend, Mailbox, MailboxPermissions, RefreshEventConsumer,
|
||||||
SpecialUsageMailbox,
|
SpecialUsageMailbox,
|
||||||
};
|
};
|
||||||
use crate::conf::AccountSettings;
|
use crate::conf::AccountSettings;
|
||||||
|
@ -54,7 +54,7 @@ unsafe impl Sync for DbWrapper {}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NotmuchDb {
|
pub struct NotmuchDb {
|
||||||
database: DbWrapper,
|
database: DbWrapper,
|
||||||
folders: Arc<RwLock<FnvHashMap<FolderHash, NotmuchFolder>>>,
|
mailboxes: Arc<RwLock<FnvHashMap<MailboxHash, NotmuchMailbox>>>,
|
||||||
index: Arc<RwLock<FnvHashMap<EnvelopeHash, &'static CStr>>>,
|
index: Arc<RwLock<FnvHashMap<EnvelopeHash, &'static CStr>>>,
|
||||||
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
|
@ -66,7 +66,7 @@ unsafe impl Sync for NotmuchDb {}
|
||||||
|
|
||||||
impl Drop for NotmuchDb {
|
impl Drop for NotmuchDb {
|
||||||
fn drop(&mut self) {
|
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() {
|
if let Some(query) = f.query.take() {
|
||||||
unsafe {
|
unsafe {
|
||||||
notmuch_query_destroy(query);
|
notmuch_query_destroy(query);
|
||||||
|
@ -82,10 +82,10 @@ impl Drop for NotmuchDb {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
struct NotmuchFolder {
|
struct NotmuchMailbox {
|
||||||
hash: FolderHash,
|
hash: MailboxHash,
|
||||||
children: Vec<FolderHash>,
|
children: Vec<MailboxHash>,
|
||||||
parent: Option<FolderHash>,
|
parent: Option<MailboxHash>,
|
||||||
name: String,
|
name: String,
|
||||||
path: String,
|
path: String,
|
||||||
query_str: String,
|
query_str: String,
|
||||||
|
@ -97,8 +97,8 @@ struct NotmuchFolder {
|
||||||
unseen: Arc<Mutex<usize>>,
|
unseen: Arc<Mutex<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BackendFolder for NotmuchFolder {
|
impl BackendMailbox for NotmuchMailbox {
|
||||||
fn hash(&self) -> FolderHash {
|
fn hash(&self) -> MailboxHash {
|
||||||
self.hash
|
self.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,15 +112,15 @@ impl BackendFolder for NotmuchFolder {
|
||||||
|
|
||||||
fn change_name(&mut self, _s: &str) {}
|
fn change_name(&mut self, _s: &str) {}
|
||||||
|
|
||||||
fn clone(&self) -> Folder {
|
fn clone(&self) -> Mailbox {
|
||||||
Box::new(std::clone::Clone::clone(self))
|
Box::new(std::clone::Clone::clone(self))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn children(&self) -> &[FolderHash] {
|
fn children(&self) -> &[MailboxHash] {
|
||||||
&self.children
|
&self.children
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parent(&self) -> Option<FolderHash> {
|
fn parent(&self) -> Option<MailboxHash> {
|
||||||
self.parent
|
self.parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,8 +128,8 @@ impl BackendFolder for NotmuchFolder {
|
||||||
*self.usage.read().unwrap()
|
*self.usage.read().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn permissions(&self) -> FolderPermissions {
|
fn permissions(&self) -> MailboxPermissions {
|
||||||
FolderPermissions::default()
|
MailboxPermissions::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_subscribed(&self) -> bool {
|
fn is_subscribed(&self) -> bool {
|
||||||
|
@ -150,8 +150,8 @@ impl BackendFolder for NotmuchFolder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Send for NotmuchFolder {}
|
unsafe impl Send for NotmuchMailbox {}
|
||||||
unsafe impl Sync for NotmuchFolder {}
|
unsafe impl Sync for NotmuchMailbox {}
|
||||||
|
|
||||||
impl NotmuchDb {
|
impl NotmuchDb {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
@ -159,11 +159,11 @@ impl NotmuchDb {
|
||||||
_is_subscribed: Box<dyn Fn(&str) -> bool>,
|
_is_subscribed: Box<dyn Fn(&str) -> bool>,
|
||||||
) -> Result<Box<dyn MailBackend>> {
|
) -> Result<Box<dyn MailBackend>> {
|
||||||
let mut database: *mut notmuch_database_t = std::ptr::null_mut();
|
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() {
|
if !path.exists() {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"\"root_folder\" {} for account {} is not a valid path.",
|
"\"root_mailbox\" {} for account {} is not a valid path.",
|
||||||
s.root_folder.as_str(),
|
s.root_mailbox.as_str(),
|
||||||
s.name()
|
s.name()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -180,22 +180,22 @@ impl NotmuchDb {
|
||||||
if status != 0 {
|
if status != 0 {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"Could not open notmuch database at path {}. notmuch_database_open returned {}.",
|
"Could not open notmuch database at path {}. notmuch_database_open returned {}.",
|
||||||
s.root_folder.as_str(),
|
s.root_mailbox.as_str(),
|
||||||
status
|
status
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
assert!(!database.is_null());
|
assert!(!database.is_null());
|
||||||
let mut folders = FnvHashMap::default();
|
let mut mailboxes = FnvHashMap::default();
|
||||||
for (k, f) in s.folders.iter() {
|
for (k, f) in s.mailboxes.iter() {
|
||||||
if let Some(query_str) = f.extra.get("query") {
|
if let Some(query_str) = f.extra.get("query") {
|
||||||
let hash = {
|
let hash = {
|
||||||
let mut h = DefaultHasher::new();
|
let mut h = DefaultHasher::new();
|
||||||
k.hash(&mut h);
|
k.hash(&mut h);
|
||||||
h.finish()
|
h.finish()
|
||||||
};
|
};
|
||||||
folders.insert(
|
mailboxes.insert(
|
||||||
hash,
|
hash,
|
||||||
NotmuchFolder {
|
NotmuchMailbox {
|
||||||
hash,
|
hash,
|
||||||
name: k.to_string(),
|
name: k.to_string(),
|
||||||
path: k.to_string(),
|
path: k.to_string(),
|
||||||
|
@ -211,7 +211,7 @@ impl NotmuchDb {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Err(MeliError::new(format!(
|
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
|
k
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -225,24 +225,24 @@ impl NotmuchDb {
|
||||||
index: Arc::new(RwLock::new(Default::default())),
|
index: Arc::new(RwLock::new(Default::default())),
|
||||||
tag_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,
|
save_messages_to: None,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn validate_config(s: &AccountSettings) -> Result<()> {
|
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() {
|
if !path.exists() {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"\"root_folder\" {} for account {} is not a valid path.",
|
"\"root_mailbox\" {} for account {} is not a valid path.",
|
||||||
s.root_folder.as_str(),
|
s.root_mailbox.as_str(),
|
||||||
s.name()
|
s.name()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
for (k, f) in s.folders.iter() {
|
for (k, f) in s.mailboxes.iter() {
|
||||||
if f.extra.get("query").is_none() {
|
if f.extra.get("query").is_none() {
|
||||||
return Err(MeliError::new(format!(
|
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
|
k
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -288,21 +288,21 @@ impl MailBackend for NotmuchDb {
|
||||||
fn is_online(&self) -> Result<()> {
|
fn is_online(&self) -> Result<()> {
|
||||||
Ok(())
|
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 mut w = AsyncBuilder::new();
|
||||||
let folder_hash = folder.hash();
|
let mailbox_hash = mailbox.hash();
|
||||||
let database = self.database.clone();
|
let database = self.database.clone();
|
||||||
let index = self.index.clone();
|
let index = self.index.clone();
|
||||||
let tag_index = self.tag_index.clone();
|
let tag_index = self.tag_index.clone();
|
||||||
let folders = self.folders.clone();
|
let mailboxes = self.mailboxes.clone();
|
||||||
let handle = {
|
let handle = {
|
||||||
let tx = w.tx();
|
let tx = w.tx();
|
||||||
let closure = move |_work_context| {
|
let closure = move |_work_context| {
|
||||||
let mut ret: Vec<Envelope> = Vec::new();
|
let mut ret: Vec<Envelope> = Vec::new();
|
||||||
let database_lck = database.inner.read().unwrap();
|
let database_lck = database.inner.read().unwrap();
|
||||||
let mut folders_lck = folders.write().unwrap();
|
let mut mailboxes_lck = mailboxes.write().unwrap();
|
||||||
let folder = folders_lck.get_mut(&folder_hash).unwrap();
|
let mailbox = mailboxes_lck.get_mut(&mailbox_hash).unwrap();
|
||||||
let query_str = std::ffi::CString::new(folder.query_str.as_str()).unwrap();
|
let query_str = std::ffi::CString::new(mailbox.query_str.as_str()).unwrap();
|
||||||
let query: *mut notmuch_query_t =
|
let query: *mut notmuch_query_t =
|
||||||
unsafe { notmuch_query_create(*database_lck, query_str.as_ptr()) };
|
unsafe { notmuch_query_create(*database_lck, query_str.as_ptr()) };
|
||||||
if query.is_null() {
|
if query.is_null() {
|
||||||
|
@ -319,7 +319,7 @@ impl MailBackend for NotmuchDb {
|
||||||
if status != 0 {
|
if status != 0 {
|
||||||
tx.send(AsyncStatus::Payload(Err(MeliError::new(format!(
|
tx.send(AsyncStatus::Payload(Err(MeliError::new(format!(
|
||||||
"Search for {} returned {}",
|
"Search for {} returned {}",
|
||||||
folder.query_str.as_str(),
|
mailbox.query_str.as_str(),
|
||||||
status,
|
status,
|
||||||
)))))
|
)))))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -383,7 +383,7 @@ impl MailBackend for NotmuchDb {
|
||||||
index.write().unwrap().remove(&env_hash);
|
index.write().unwrap().remove(&env_hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
folder.query = Some(query);
|
mailbox.query = Some(query);
|
||||||
tx.send(AsyncStatus::Payload(Ok(ret))).unwrap();
|
tx.send(AsyncStatus::Payload(Ok(ret))).unwrap();
|
||||||
tx.send(AsyncStatus::Finished).unwrap();
|
tx.send(AsyncStatus::Finished).unwrap();
|
||||||
};
|
};
|
||||||
|
@ -405,13 +405,13 @@ impl MailBackend for NotmuchDb {
|
||||||
.spawn(move || {})?;
|
.spawn(move || {})?;
|
||||||
Ok(handle.thread().id())
|
Ok(handle.thread().id())
|
||||||
}
|
}
|
||||||
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
|
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.folders
|
.mailboxes
|
||||||
.read()
|
.read()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, f)| (*k, BackendFolder::clone(f)))
|
.map(|(k, f)| (*k, BackendMailbox::clone(f)))
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp> {
|
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
|
let mut path = self
|
||||||
.save_messages_to
|
.save_messages_to
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -435,7 +435,7 @@ impl MailBackend for NotmuchDb {
|
||||||
path.push(d);
|
path.push(d);
|
||||||
if !path.is_dir() {
|
if !path.is_dir() {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"{} is not a valid maildir folder",
|
"{} is not a valid maildir mailbox",
|
||||||
path.display()
|
path.display()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
@ -443,7 +443,7 @@ impl MailBackend for NotmuchDb {
|
||||||
}
|
}
|
||||||
path.push("cur");
|
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 {
|
fn as_any(&self) -> &dyn::std::any::Any {
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::backends::FolderHash;
|
use crate::backends::MailboxHash;
|
||||||
use core::ops::{Index, IndexMut};
|
use core::ops::{Index, IndexMut};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
@ -67,9 +67,9 @@ pub struct Collection {
|
||||||
message_ids: FnvHashMap<Vec<u8>, EnvelopeHash>,
|
message_ids: FnvHashMap<Vec<u8>, EnvelopeHash>,
|
||||||
date_index: BTreeMap<UnixTimestamp, EnvelopeHash>,
|
date_index: BTreeMap<UnixTimestamp, EnvelopeHash>,
|
||||||
subject_index: Option<BTreeMap<String, EnvelopeHash>>,
|
subject_index: Option<BTreeMap<String, EnvelopeHash>>,
|
||||||
pub threads: FnvHashMap<FolderHash, Threads>,
|
pub threads: FnvHashMap<MailboxHash, Threads>,
|
||||||
sent_folder: Option<FolderHash>,
|
sent_mailbox: Option<MailboxHash>,
|
||||||
pub mailboxes: FnvHashMap<FolderHash, FnvHashSet<EnvelopeHash>>,
|
pub mailboxes: FnvHashMap<MailboxHash, FnvHashSet<EnvelopeHash>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Collection {
|
impl Drop for Collection {
|
||||||
|
@ -112,7 +112,7 @@ impl Collection {
|
||||||
subject_index,
|
subject_index,
|
||||||
threads,
|
threads,
|
||||||
mailboxes,
|
mailboxes,
|
||||||
sent_folder: None,
|
sent_mailbox: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,18 +124,18 @@ impl Collection {
|
||||||
self.envelopes.read().unwrap().is_empty()
|
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);
|
debug!("DEBUG: Removing {}", envelope_hash);
|
||||||
self.envelopes.write().unwrap().remove(&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);
|
m.remove(&envelope_hash);
|
||||||
});
|
});
|
||||||
self.threads
|
self.threads
|
||||||
.entry(folder_hash)
|
.entry(mailbox_hash)
|
||||||
.or_default()
|
.or_default()
|
||||||
.remove(envelope_hash);
|
.remove(envelope_hash);
|
||||||
for (h, t) in self.threads.iter_mut() {
|
for (h, t) in self.threads.iter_mut() {
|
||||||
if *h == folder_hash {
|
if *h == mailbox_hash {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
t.remove(envelope_hash);
|
t.remove(envelope_hash);
|
||||||
|
@ -146,13 +146,13 @@ impl Collection {
|
||||||
&mut self,
|
&mut self,
|
||||||
old_hash: EnvelopeHash,
|
old_hash: EnvelopeHash,
|
||||||
new_hash: EnvelopeHash,
|
new_hash: EnvelopeHash,
|
||||||
folder_hash: FolderHash,
|
mailbox_hash: MailboxHash,
|
||||||
) {
|
) {
|
||||||
if !self.envelopes.write().unwrap().contains_key(&old_hash) {
|
if !self.envelopes.write().unwrap().contains_key(&old_hash) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut envelope = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
|
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.remove(&old_hash);
|
||||||
m.insert(new_hash);
|
m.insert(new_hash);
|
||||||
});
|
});
|
||||||
|
@ -163,7 +163,7 @@ impl Collection {
|
||||||
{
|
{
|
||||||
if self
|
if self
|
||||||
.threads
|
.threads
|
||||||
.entry(folder_hash)
|
.entry(mailbox_hash)
|
||||||
.or_default()
|
.or_default()
|
||||||
.update_envelope(&self.envelopes, old_hash, new_hash)
|
.update_envelope(&self.envelopes, old_hash, new_hash)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
|
@ -173,11 +173,11 @@ impl Collection {
|
||||||
}
|
}
|
||||||
/* envelope is not in threads, so insert it */
|
/* envelope is not in threads, so insert it */
|
||||||
self.threads
|
self.threads
|
||||||
.entry(folder_hash)
|
.entry(mailbox_hash)
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(&mut self.envelopes, new_hash);
|
.insert(&mut self.envelopes, new_hash);
|
||||||
for (h, t) in self.threads.iter_mut() {
|
for (h, t) in self.threads.iter_mut() {
|
||||||
if *h == folder_hash {
|
if *h == mailbox_hash {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
t.update_envelope(&self.envelopes, old_hash, new_hash)
|
t.update_envelope(&self.envelopes, old_hash, new_hash)
|
||||||
|
@ -187,14 +187,14 @@ impl Collection {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Merge new mailbox to collection and update threads.
|
/// 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(
|
pub fn merge(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut new_envelopes: FnvHashMap<EnvelopeHash, Envelope>,
|
mut new_envelopes: FnvHashMap<EnvelopeHash, Envelope>,
|
||||||
folder_hash: FolderHash,
|
mailbox_hash: MailboxHash,
|
||||||
sent_folder: Option<FolderHash>,
|
sent_mailbox: Option<MailboxHash>,
|
||||||
) -> Option<SmallVec<[FolderHash; 8]>> {
|
) -> Option<SmallVec<[MailboxHash; 8]>> {
|
||||||
self.sent_folder = sent_folder;
|
self.sent_mailbox = sent_mailbox;
|
||||||
for (h, e) in new_envelopes.iter() {
|
for (h, e) in new_envelopes.iter() {
|
||||||
self.message_ids.insert(e.message_id().raw().to_vec(), *h);
|
self.message_ids.insert(e.message_id().raw().to_vec(), *h);
|
||||||
}
|
}
|
||||||
|
@ -203,21 +203,21 @@ impl Collection {
|
||||||
ref mut threads,
|
ref mut threads,
|
||||||
ref mut envelopes,
|
ref mut envelopes,
|
||||||
ref mut mailboxes,
|
ref mut mailboxes,
|
||||||
ref sent_folder,
|
ref sent_mailbox,
|
||||||
..
|
..
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
if !threads.contains_key(&folder_hash) {
|
if !threads.contains_key(&mailbox_hash) {
|
||||||
threads.insert(folder_hash, Threads::new(new_envelopes.len()));
|
threads.insert(mailbox_hash, Threads::new(new_envelopes.len()));
|
||||||
mailboxes.insert(folder_hash, new_envelopes.keys().cloned().collect());
|
mailboxes.insert(mailbox_hash, new_envelopes.keys().cloned().collect());
|
||||||
for (h, e) in new_envelopes {
|
for (h, e) in new_envelopes {
|
||||||
envelopes.write().unwrap().insert(h, e);
|
envelopes.write().unwrap().insert(h, e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mailboxes.entry(folder_hash).and_modify(|m| {
|
mailboxes.entry(mailbox_hash).and_modify(|m| {
|
||||||
m.extend(new_envelopes.keys().cloned());
|
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 =
|
let mut ordered_hash_set =
|
||||||
new_envelopes.keys().cloned().collect::<Vec<EnvelopeHash>>();
|
new_envelopes.keys().cloned().collect::<Vec<EnvelopeHash>>();
|
||||||
ordered_hash_set.sort_by(|a, b| {
|
ordered_hash_set.sort_by(|a, b| {
|
||||||
|
@ -237,14 +237,14 @@ impl Collection {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ret = SmallVec::new();
|
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 {
|
for t_fh in keys {
|
||||||
if t_fh == folder_hash {
|
if t_fh == mailbox_hash {
|
||||||
continue;
|
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 envelopes_lck = envelopes.read().unwrap();
|
||||||
let mut ordered_hash_set = threads[&folder_hash]
|
let mut ordered_hash_set = threads[&mailbox_hash]
|
||||||
.hash_set
|
.hash_set
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
|
@ -265,7 +265,7 @@ impl Collection {
|
||||||
}
|
}
|
||||||
continue;
|
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 envelopes_lck = envelopes.read().unwrap();
|
||||||
let mut ordered_hash_set = threads[&t_fh]
|
let mut ordered_hash_set = threads[&t_fh]
|
||||||
.hash_set
|
.hash_set
|
||||||
|
@ -282,12 +282,12 @@ impl Collection {
|
||||||
let mut updated = false;
|
let mut updated = false;
|
||||||
for h in ordered_hash_set {
|
for h in ordered_hash_set {
|
||||||
updated |= threads
|
updated |= threads
|
||||||
.entry(folder_hash)
|
.entry(mailbox_hash)
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert_reply(envelopes, h);
|
.insert_reply(envelopes, h);
|
||||||
}
|
}
|
||||||
if updated {
|
if updated {
|
||||||
ret.push(folder_hash);
|
ret.push(mailbox_hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -302,19 +302,23 @@ impl Collection {
|
||||||
&mut self,
|
&mut self,
|
||||||
old_hash: EnvelopeHash,
|
old_hash: EnvelopeHash,
|
||||||
mut envelope: Envelope,
|
mut envelope: Envelope,
|
||||||
folder_hash: FolderHash,
|
mailbox_hash: MailboxHash,
|
||||||
) {
|
) {
|
||||||
let old_env = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
|
let old_env = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
|
||||||
envelope.set_thread(old_env.thread());
|
envelope.set_thread(old_env.thread());
|
||||||
let new_hash = envelope.hash();
|
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.remove(&old_hash);
|
||||||
m.insert(new_hash);
|
m.insert(new_hash);
|
||||||
});
|
});
|
||||||
self.message_ids
|
self.message_ids
|
||||||
.insert(envelope.message_id().raw().to_vec(), new_hash);
|
.insert(envelope.message_id().raw().to_vec(), new_hash);
|
||||||
self.envelopes.write().unwrap().insert(new_hash, envelope);
|
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() {
|
for (_, t) in self.threads.iter_mut() {
|
||||||
t.update_envelope(&self.envelopes, old_hash, new_hash)
|
t.update_envelope(&self.envelopes, old_hash, new_hash)
|
||||||
.unwrap_or(());
|
.unwrap_or(());
|
||||||
|
@ -323,7 +327,7 @@ impl Collection {
|
||||||
{
|
{
|
||||||
if self
|
if self
|
||||||
.threads
|
.threads
|
||||||
.entry(folder_hash)
|
.entry(mailbox_hash)
|
||||||
.or_default()
|
.or_default()
|
||||||
.update_envelope(&self.envelopes, old_hash, new_hash)
|
.update_envelope(&self.envelopes, old_hash, new_hash)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
|
@ -333,11 +337,11 @@ impl Collection {
|
||||||
}
|
}
|
||||||
/* envelope is not in threads, so insert it */
|
/* envelope is not in threads, so insert it */
|
||||||
self.threads
|
self.threads
|
||||||
.entry(folder_hash)
|
.entry(mailbox_hash)
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(&mut self.envelopes, new_hash);
|
.insert(&mut self.envelopes, new_hash);
|
||||||
for (h, t) in self.threads.iter_mut() {
|
for (h, t) in self.threads.iter_mut() {
|
||||||
if *h == folder_hash {
|
if *h == mailbox_hash {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
t.update_envelope(&self.envelopes, old_hash, new_hash)
|
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();
|
let hash = envelope.hash();
|
||||||
self.mailboxes.entry(folder_hash).and_modify(|m| {
|
self.mailboxes.entry(mailbox_hash).and_modify(|m| {
|
||||||
m.insert(hash);
|
m.insert(hash);
|
||||||
});
|
});
|
||||||
self.message_ids
|
self.message_ids
|
||||||
.insert(envelope.message_id().raw().to_vec(), hash);
|
.insert(envelope.message_id().raw().to_vec(), hash);
|
||||||
self.envelopes.write().unwrap().insert(hash, envelope);
|
self.envelopes.write().unwrap().insert(hash, envelope);
|
||||||
self.threads
|
self.threads
|
||||||
.entry(folder_hash)
|
.entry(mailbox_hash)
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(&mut self.envelopes, hash);
|
.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);
|
self.insert_reply(hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -385,15 +393,15 @@ impl Collection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<&FolderHash> for Collection {
|
impl Index<&MailboxHash> for Collection {
|
||||||
type Output = FnvHashSet<EnvelopeHash>;
|
type Output = FnvHashSet<EnvelopeHash>;
|
||||||
fn index(&self, index: &FolderHash) -> &FnvHashSet<EnvelopeHash> {
|
fn index(&self, index: &MailboxHash) -> &FnvHashSet<EnvelopeHash> {
|
||||||
&self.mailboxes[index]
|
&self.mailboxes[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexMut<&FolderHash> for Collection {
|
impl IndexMut<&MailboxHash> for Collection {
|
||||||
fn index_mut(&mut self, index: &FolderHash) -> &mut FnvHashSet<EnvelopeHash> {
|
fn index_mut(&mut self, index: &MailboxHash) -> &mut FnvHashSet<EnvelopeHash> {
|
||||||
self.mailboxes.get_mut(index).unwrap()
|
self.mailboxes.get_mut(index).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,14 +25,14 @@ use std::collections::HashMap;
|
||||||
#[derive(Debug, Serialize, Default, Clone)]
|
#[derive(Debug, Serialize, Default, Clone)]
|
||||||
pub struct AccountSettings {
|
pub struct AccountSettings {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub root_folder: String,
|
pub root_mailbox: String,
|
||||||
pub format: String,
|
pub format: String,
|
||||||
pub identity: String,
|
pub identity: String,
|
||||||
pub read_only: bool,
|
pub read_only: bool,
|
||||||
pub display_name: Option<String>,
|
pub display_name: Option<String>,
|
||||||
pub subscribed_folders: Vec<String>,
|
pub subscribed_mailboxes: Vec<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub folders: HashMap<String, FolderConf>,
|
pub mailboxes: HashMap<String, MailboxConf>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub manual_refresh: bool,
|
pub manual_refresh: bool,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -49,8 +49,8 @@ impl AccountSettings {
|
||||||
pub fn set_name(&mut self, s: String) {
|
pub fn set_name(&mut self, s: String) {
|
||||||
self.name = s;
|
self.name = s;
|
||||||
}
|
}
|
||||||
pub fn root_folder(&self) -> &str {
|
pub fn root_mailbox(&self) -> &str {
|
||||||
&self.root_folder
|
&self.root_mailbox
|
||||||
}
|
}
|
||||||
pub fn identity(&self) -> &str {
|
pub fn identity(&self) -> &str {
|
||||||
&self.identity
|
&self.identity
|
||||||
|
@ -62,8 +62,8 @@ impl AccountSettings {
|
||||||
self.display_name.as_ref()
|
self.display_name.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subscribed_folders(&self) -> &Vec<String> {
|
pub fn subscribed_mailboxes(&self) -> &Vec<String> {
|
||||||
&self.subscribed_folders
|
&self.subscribed_mailboxes
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "vcard")]
|
#[cfg(feature = "vcard")]
|
||||||
|
@ -74,7 +74,7 @@ impl AccountSettings {
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct FolderConf {
|
pub struct MailboxConf {
|
||||||
pub alias: Option<String>,
|
pub alias: Option<String>,
|
||||||
#[serde(default = "true_val")]
|
#[serde(default = "true_val")]
|
||||||
pub autoload: bool,
|
pub autoload: bool,
|
||||||
|
@ -88,9 +88,9 @@ pub struct FolderConf {
|
||||||
pub extra: HashMap<String, String>,
|
pub extra: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for FolderConf {
|
impl Default for MailboxConf {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
FolderConf {
|
MailboxConf {
|
||||||
alias: None,
|
alias: None,
|
||||||
autoload: true,
|
autoload: true,
|
||||||
subscribe: ToggleFlag::Unset,
|
subscribe: ToggleFlag::Unset,
|
||||||
|
@ -101,7 +101,7 @@ impl Default for FolderConf {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FolderConf {
|
impl MailboxConf {
|
||||||
pub fn alias(&self) -> Option<&str> {
|
pub fn alias(&self) -> Option<&str> {
|
||||||
self.alias.as_ref().map(String::as_str)
|
self.alias.as_ref().map(String::as_str)
|
||||||
}
|
}
|
||||||
|
|
|
@ -650,7 +650,7 @@ impl Threads {
|
||||||
&mut self,
|
&mut self,
|
||||||
envelopes: &mut Envelopes,
|
envelopes: &mut Envelopes,
|
||||||
env_hash: EnvelopeHash,
|
env_hash: EnvelopeHash,
|
||||||
other_folder: bool,
|
other_mailbox: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let envelopes_lck = envelopes.read().unwrap();
|
let envelopes_lck = envelopes.read().unwrap();
|
||||||
let reply_to_id: Option<ThreadNodeHash> = envelopes_lck[&env_hash]
|
let reply_to_id: Option<ThreadNodeHash> = envelopes_lck[&env_hash]
|
||||||
|
@ -664,7 +664,7 @@ impl Threads {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if other_folder
|
if other_mailbox
|
||||||
&& reply_to_id.is_none()
|
&& reply_to_id.is_none()
|
||||||
&& !self.message_ids.contains_key(message_id)
|
&& !self.message_ids.contains_key(message_id)
|
||||||
&& !envelopes_lck[&env_hash]
|
&& !envelopes_lck[&env_hash]
|
||||||
|
|
|
@ -9,29 +9,29 @@
|
||||||
#
|
#
|
||||||
# Setting up a Maildir account
|
# Setting up a Maildir account
|
||||||
#[accounts.account-name]
|
#[accounts.account-name]
|
||||||
#root_folder = "/path/to/root/folder"
|
#root_mailbox = "/path/to/root/mailbox"
|
||||||
#format = "Maildir"
|
#format = "Maildir"
|
||||||
#index_style = "Conversations" # or [plain, threaded, compact]
|
#index_style = "Conversations" # or [plain, threaded, compact]
|
||||||
#identity="email@address.tld"
|
#identity="email@address.tld"
|
||||||
#display_name = "Name"
|
#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
|
# Set mailbox-specific settings
|
||||||
# [accounts.account-name.folders]
|
# [accounts.account-name.mailboxes]
|
||||||
# "INBOX" = { rename="Inbox" }
|
# "INBOX" = { rename="Inbox" }
|
||||||
# "drafts" = { rename="Drafts" }
|
# "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
|
# Setting up an mbox account
|
||||||
#[accounts.mbox]
|
#[accounts.mbox]
|
||||||
#root_folder = "/var/mail/username"
|
#root_mailbox = "/var/mail/username"
|
||||||
#format = "mbox"
|
#format = "mbox"
|
||||||
#index_style = "Compact"
|
#index_style = "Compact"
|
||||||
#identity="username@hostname.local"
|
#identity="username@hostname.local"
|
||||||
#
|
#
|
||||||
# Setting up an IMAP account
|
# Setting up an IMAP account
|
||||||
#[accounts."imap"]
|
#[accounts."imap"]
|
||||||
#root_folder = "INBOX"
|
#root_mailbox = "INBOX"
|
||||||
#format = "imap"
|
#format = "imap"
|
||||||
#server_hostname="mail.server.tld"
|
#server_hostname="mail.server.tld"
|
||||||
#server_password="pha2hiLohs2eeeish2phaii1We3ood4chakaiv0hien2ahie3m"
|
#server_password="pha2hiLohs2eeeish2phaii1We3ood4chakaiv0hien2ahie3m"
|
||||||
|
@ -42,20 +42,20 @@
|
||||||
#index_style = "Conversations"
|
#index_style = "Conversations"
|
||||||
#identity = "username@server.tld"
|
#identity = "username@server.tld"
|
||||||
#display_name = "Name Name"
|
#display_name = "Name Name"
|
||||||
## match every folder:
|
## match every mailbox:
|
||||||
#subscribed_folders = ["*" ]
|
#subscribed_mailboxes = ["*" ]
|
||||||
## match specific folders:
|
## match specific mailboxes:
|
||||||
#subscribed_folders = ["INBOX", "INBOX/Sent", "INBOX/Drafts", "INBOX/Junk"]
|
#subscribed_mailboxes = ["INBOX", "INBOX/Sent", "INBOX/Drafts", "INBOX/Junk"]
|
||||||
|
|
||||||
# Setting up an account for an already existing notmuch database
|
# Setting up an account for an already existing notmuch database
|
||||||
#[accounts.notmuch]
|
#[accounts.notmuch]
|
||||||
#root_folder = "/path/to/folder" # where .notmuch/ directory is located
|
#root_mailbox = "/path/to/folder" # where .notmuch/ directory is located
|
||||||
#format = "notmuch"
|
#format = "notmuch"
|
||||||
#index_style = "conversations"
|
#index_style = "conversations"
|
||||||
#identity="username@server.tld"
|
#identity="username@server.tld"
|
||||||
#display_name = "Name Name"
|
#display_name = "Name Name"
|
||||||
# # notmuch folders are virtual, they are defined by their alias and the notmuch query that corresponds to their content.
|
# # notmuch mailboxes are virtual, they are defined by their alias and the notmuch query that corresponds to their content.
|
||||||
# [accounts.notmuch.folders]
|
# [accounts.notmuch.mailboxes]
|
||||||
# "INBOX" = { query="tag:inbox", subscribe = true }
|
# "INBOX" = { query="tag:inbox", subscribe = true }
|
||||||
# "Drafts" = { query="tag:draft", subscribe = true }
|
# "Drafts" = { query="tag:draft", subscribe = true }
|
||||||
# "Sent" = { query="from:username@server.tld from:username2@server.tld", subscribe = true }
|
# "Sent" = { query="from:username@server.tld from:username2@server.tld", subscribe = true }
|
||||||
|
@ -88,8 +88,8 @@
|
||||||
#[shortcuts.listing]
|
#[shortcuts.listing]
|
||||||
#prev_page = "PageUp"
|
#prev_page = "PageUp"
|
||||||
#next_page = "PageDown"
|
#next_page = "PageDown"
|
||||||
#prev_folder = 'K'
|
#prev_mailbox = 'K'
|
||||||
#next_folder = 'J'
|
#next_mailbox = 'J'
|
||||||
#prev_account = 'l'
|
#prev_account = 'l'
|
||||||
#next_account = 'h'
|
#next_account = 'h'
|
||||||
#new_mail = 'm'
|
#new_mail = 'm'
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
use melib::parsec::*;
|
use melib::parsec::*;
|
||||||
use melib::UnixTimestamp;
|
use melib::UnixTimestamp;
|
||||||
use melib::{
|
use melib::{
|
||||||
backends::{FolderHash, MailBackend},
|
backends::{MailBackend, MailboxHash},
|
||||||
email::EnvelopeHash,
|
email::EnvelopeHash,
|
||||||
thread::{SortField, SortOrder},
|
thread::{SortField, SortOrder},
|
||||||
Result,
|
Result,
|
||||||
|
@ -429,7 +429,7 @@ pub fn query_to_imap(q: &Query) -> String {
|
||||||
pub fn imap_search(
|
pub fn imap_search(
|
||||||
term: &str,
|
term: &str,
|
||||||
(_sort_field, _sort_order): (SortField, SortOrder),
|
(_sort_field, _sort_order): (SortField, SortOrder),
|
||||||
folder_hash: FolderHash,
|
mailbox_hash: MailboxHash,
|
||||||
backend: &Arc<RwLock<Box<dyn MailBackend>>>,
|
backend: &Arc<RwLock<Box<dyn MailBackend>>>,
|
||||||
) -> Result<smallvec::SmallVec<[EnvelopeHash; 512]>> {
|
) -> Result<smallvec::SmallVec<[EnvelopeHash; 512]>> {
|
||||||
let query = query().parse(term)?.1;
|
let query = query().parse(term)?.1;
|
||||||
|
@ -437,7 +437,7 @@ pub fn imap_search(
|
||||||
|
|
||||||
let b = (*backend_lck).as_any();
|
let b = (*backend_lck).as_any();
|
||||||
if let Some(imap_backend) = b.downcast_ref::<melib::backends::ImapType>() {
|
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 {
|
} else {
|
||||||
panic!("Could not downcast ImapType backend. BUG");
|
panic!("Could not downcast ImapType backend. BUG");
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
/*! Entities that handle Mail specific functions.
|
/*! Entities that handle Mail specific functions.
|
||||||
*/
|
*/
|
||||||
use super::*;
|
use super::*;
|
||||||
use melib::backends::Folder;
|
use melib::backends::Mailbox;
|
||||||
use melib::backends::FolderHash;
|
use melib::backends::MailboxHash;
|
||||||
use melib::thread::ThreadNodeHash;
|
use melib::thread::ThreadNodeHash;
|
||||||
|
|
||||||
pub mod listing;
|
pub mod listing;
|
||||||
|
|
|
@ -63,7 +63,7 @@ impl std::ops::DerefMut for EmbedStatus {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Composer {
|
pub struct Composer {
|
||||||
reply_context: Option<(FolderHash, EnvelopeHash)>,
|
reply_context: Option<(MailboxHash, EnvelopeHash)>,
|
||||||
account_cursor: usize,
|
account_cursor: usize,
|
||||||
|
|
||||||
cursor: Cursor,
|
cursor: Cursor,
|
||||||
|
@ -164,7 +164,7 @@ impl Composer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_context(
|
pub fn with_context(
|
||||||
coordinates: (usize, FolderHash),
|
coordinates: (usize, MailboxHash),
|
||||||
msg: EnvelopeHash,
|
msg: EnvelopeHash,
|
||||||
context: &Context,
|
context: &Context,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -1092,7 +1092,7 @@ pub fn send_draft(
|
||||||
context: &mut Context,
|
context: &mut Context,
|
||||||
account_cursor: usize,
|
account_cursor: usize,
|
||||||
mut draft: Draft,
|
mut draft: Draft,
|
||||||
folder_type: SpecialUsageMailbox,
|
mailbox_type: SpecialUsageMailbox,
|
||||||
flags: Flag,
|
flags: Flag,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
@ -1229,7 +1229,7 @@ pub fn send_draft(
|
||||||
save_draft(
|
save_draft(
|
||||||
bytes.as_bytes(),
|
bytes.as_bytes(),
|
||||||
context,
|
context,
|
||||||
folder_type,
|
mailbox_type,
|
||||||
flags,
|
flags,
|
||||||
account_cursor,
|
account_cursor,
|
||||||
);
|
);
|
||||||
|
@ -1239,12 +1239,12 @@ pub fn send_draft(
|
||||||
pub fn save_draft(
|
pub fn save_draft(
|
||||||
bytes: &[u8],
|
bytes: &[u8],
|
||||||
context: &mut Context,
|
context: &mut Context,
|
||||||
folder_type: SpecialUsageMailbox,
|
mailbox_type: SpecialUsageMailbox,
|
||||||
flags: Flag,
|
flags: Flag,
|
||||||
account_cursor: usize,
|
account_cursor: usize,
|
||||||
) {
|
) {
|
||||||
if let Err(MeliError { summary, details }) =
|
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(
|
context.replies.push_back(UIEvent::Notification(
|
||||||
summary.map(|s| s.into()),
|
summary.map(|s| s.into()),
|
||||||
|
|
|
@ -125,7 +125,7 @@ struct AccountMenuEntry {
|
||||||
name: String,
|
name: String,
|
||||||
// Index in the config account vector.
|
// Index in the config account vector.
|
||||||
index: usize,
|
index: usize,
|
||||||
entries: SmallVec<[(usize, FolderHash); 16]>,
|
entries: SmallVec<[(usize, MailboxHash); 16]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MailListingTrait: ListingTrait {
|
pub trait MailListingTrait: ListingTrait {
|
||||||
|
@ -137,10 +137,10 @@ pub trait MailListingTrait: ListingTrait {
|
||||||
) {
|
) {
|
||||||
let account = &mut context.accounts[self.coordinates().0];
|
let account = &mut context.accounts[self.coordinates().0];
|
||||||
let mut envs_to_set: SmallVec<[EnvelopeHash; 8]> = SmallVec::new();
|
let mut envs_to_set: SmallVec<[EnvelopeHash; 8]> = SmallVec::new();
|
||||||
let folder_hash = self.coordinates().1;
|
let mailbox_hash = self.coordinates().1;
|
||||||
for (_, h) in account.collection.threads[&folder_hash].thread_group_iter(thread_hash) {
|
for (_, h) in account.collection.threads[&mailbox_hash].thread_group_iter(thread_hash) {
|
||||||
envs_to_set.push(
|
envs_to_set.push(
|
||||||
account.collection.threads[&folder_hash].thread_nodes()[&h]
|
account.collection.threads[&mailbox_hash].thread_nodes()[&h]
|
||||||
.message()
|
.message()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
|
@ -210,8 +210,8 @@ pub trait MailListingTrait: ListingTrait {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ListingTrait: Component {
|
pub trait ListingTrait: Component {
|
||||||
fn coordinates(&self) -> (usize, FolderHash);
|
fn coordinates(&self) -> (usize, MailboxHash);
|
||||||
fn set_coordinates(&mut self, _: (usize, FolderHash));
|
fn set_coordinates(&mut self, _: (usize, MailboxHash));
|
||||||
fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context);
|
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 highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context);
|
||||||
fn filter(&mut self, _filter_term: &str, _context: &Context) {}
|
fn filter(&mut self, _filter_term: &str, _context: &Context) {}
|
||||||
|
@ -446,11 +446,11 @@ impl Component for Listing {
|
||||||
self.change_account(context);
|
self.change_account(context);
|
||||||
} else {
|
} else {
|
||||||
self.accounts[*account_index].entries = context.accounts[*account_index]
|
self.accounts[*account_index].entries = context.accounts[*account_index]
|
||||||
.list_folders()
|
.list_mailboxes()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|folder_node| {
|
.filter(|mailbox_node| {
|
||||||
context.accounts[*account_index][&folder_node.hash]
|
context.accounts[*account_index][&mailbox_node.hash]
|
||||||
.ref_folder
|
.ref_mailbox
|
||||||
.is_subscribed()
|
.is_subscribed()
|
||||||
})
|
})
|
||||||
.map(|f| (f.depth, f.hash))
|
.map(|f| (f.depth, f.hash))
|
||||||
|
@ -459,14 +459,14 @@ impl Component for Listing {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
UIEvent::MailboxDelete((account_index, _folder_hash))
|
UIEvent::MailboxDelete((account_index, _mailbox_hash))
|
||||||
| UIEvent::MailboxCreate((account_index, _folder_hash)) => {
|
| UIEvent::MailboxCreate((account_index, _mailbox_hash)) => {
|
||||||
self.accounts[*account_index].entries = context.accounts[*account_index]
|
self.accounts[*account_index].entries = context.accounts[*account_index]
|
||||||
.list_folders()
|
.list_mailboxes()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|folder_node| {
|
.filter(|mailbox_node| {
|
||||||
context.accounts[*account_index][&folder_node.hash]
|
context.accounts[*account_index][&mailbox_node.hash]
|
||||||
.ref_folder
|
.ref_mailbox
|
||||||
.is_subscribed()
|
.is_subscribed()
|
||||||
})
|
})
|
||||||
.map(|f| (f.depth, f.hash))
|
.map(|f| (f.depth, f.hash))
|
||||||
|
@ -495,8 +495,8 @@ impl Component for Listing {
|
||||||
let shortcuts = self.get_shortcuts(context);
|
let shortcuts = self.get_shortcuts(context);
|
||||||
match *event {
|
match *event {
|
||||||
UIEvent::Input(ref k)
|
UIEvent::Input(ref k)
|
||||||
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_folder"])
|
if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_mailbox"])
|
||||||
|| shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_folder"]) =>
|
|| shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_mailbox"]) =>
|
||||||
{
|
{
|
||||||
let amount = if self.cmd_buf.is_empty() {
|
let amount = if self.cmd_buf.is_empty() {
|
||||||
1
|
1
|
||||||
|
@ -514,28 +514,28 @@ impl Component for Listing {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
match k {
|
match k {
|
||||||
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_folder"]) => {
|
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_mailbox"]) => {
|
||||||
if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0]
|
if let Some((_, mailbox_hash)) = self.accounts[self.cursor_pos.0]
|
||||||
.entries
|
.entries
|
||||||
.get(self.cursor_pos.1 + amount)
|
.get(self.cursor_pos.1 + amount)
|
||||||
{
|
{
|
||||||
self.cursor_pos.1 += amount;
|
self.cursor_pos.1 += amount;
|
||||||
self.component
|
self.component
|
||||||
.set_coordinates((self.cursor_pos.0, *folder_hash));
|
.set_coordinates((self.cursor_pos.0, *mailbox_hash));
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
} else {
|
} else {
|
||||||
return true;
|
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 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
|
.entries
|
||||||
.get(self.cursor_pos.1 - amount)
|
.get(self.cursor_pos.1 - amount)
|
||||||
{
|
{
|
||||||
self.cursor_pos.1 -= amount;
|
self.cursor_pos.1 -= amount;
|
||||||
self.component
|
self.component
|
||||||
.set_coordinates((self.cursor_pos.0, *folder_hash));
|
.set_coordinates((self.cursor_pos.0, *mailbox_hash));
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
} else {
|
} else {
|
||||||
return true;
|
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
|
.entries
|
||||||
.get(self.cursor_pos.1)
|
.get(self.cursor_pos.1)
|
||||||
{
|
{
|
||||||
/* Account might have no folders yet if it's offline */
|
/* Account might have no mailboxes yet if it's offline */
|
||||||
/* Check if per-folder configuration overrides general configuration */
|
/* Check if per-mailbox configuration overrides general configuration */
|
||||||
if let Some(index_style) = context
|
if let Some(index_style) = context
|
||||||
.accounts
|
.accounts
|
||||||
.get(self.cursor_pos.0)
|
.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);
|
self.component.set_style(index_style);
|
||||||
} else if let Some(index_style) = context
|
} else if let Some(index_style) = context
|
||||||
|
@ -642,12 +642,12 @@ impl Component for Listing {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Action::ViewMailbox(idx) => {
|
Action::ViewMailbox(idx) => {
|
||||||
if let Some((_, folder_hash)) =
|
if let Some((_, mailbox_hash)) =
|
||||||
self.accounts[self.cursor_pos.0].entries.get(*idx)
|
self.accounts[self.cursor_pos.0].entries.get(*idx)
|
||||||
{
|
{
|
||||||
self.cursor_pos.1 = *idx;
|
self.cursor_pos.1 = *idx;
|
||||||
self.component
|
self.component
|
||||||
.set_coordinates((self.cursor_pos.0, *folder_hash));
|
.set_coordinates((self.cursor_pos.0, *mailbox_hash));
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
|
@ -791,8 +791,8 @@ impl Component for Listing {
|
||||||
if shortcut!(key == shortcuts[Listing::DESCRIPTION]["refresh"]) =>
|
if shortcut!(key == shortcuts[Listing::DESCRIPTION]["refresh"]) =>
|
||||||
{
|
{
|
||||||
let account = &mut context.accounts[self.cursor_pos.0];
|
let account = &mut context.accounts[self.cursor_pos.0];
|
||||||
if let Some(&folder_hash) = account.folders_order.get(self.cursor_pos.1) {
|
if let Some(&mailbox_hash) = account.mailboxes_order.get(self.cursor_pos.1) {
|
||||||
if let Err(err) = account.refresh(folder_hash) {
|
if let Err(err) = account.refresh(mailbox_hash) {
|
||||||
context.replies.push_back(UIEvent::Notification(
|
context.replies.push_back(UIEvent::Notification(
|
||||||
Some("Could not refresh.".to_string()),
|
Some("Could not refresh.".to_string()),
|
||||||
err.to_string(),
|
err.to_string(),
|
||||||
|
@ -862,36 +862,36 @@ impl Component for Listing {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_status(&self, context: &Context) -> String {
|
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
|
.entries
|
||||||
.get(self.cursor_pos.1)
|
.get(self.cursor_pos.1)
|
||||||
{
|
{
|
||||||
*folder_hash
|
*mailbox_hash
|
||||||
} else {
|
} else {
|
||||||
return String::new();
|
return String::new();
|
||||||
};
|
};
|
||||||
|
|
||||||
let account = &context.accounts[self.cursor_pos.0];
|
let account = &context.accounts[self.cursor_pos.0];
|
||||||
use crate::conf::accounts::MailboxStatus;
|
use crate::conf::accounts::MailboxStatus;
|
||||||
match account[&folder_hash].status {
|
match account[&mailbox_hash].status {
|
||||||
MailboxStatus::Available | MailboxStatus::Parsing(_, _) => format!(
|
MailboxStatus::Available | MailboxStatus::Parsing(_, _) => format!(
|
||||||
"Mailbox: {}, Messages: {}, New: {}",
|
"Mailbox: {}, Messages: {}, New: {}",
|
||||||
account[&folder_hash].ref_folder.name(),
|
account[&mailbox_hash].ref_mailbox.name(),
|
||||||
account.collection[&folder_hash].len(),
|
account.collection[&mailbox_hash].len(),
|
||||||
account[&folder_hash]
|
account[&mailbox_hash]
|
||||||
.ref_folder
|
.ref_mailbox
|
||||||
.count()
|
.count()
|
||||||
.ok()
|
.ok()
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
.unwrap_or(0),
|
.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 {
|
impl From<(IndexStyle, (usize, MailboxHash))> for ListingComponent {
|
||||||
fn from((index_style, coordinates): (IndexStyle, (usize, FolderHash))) -> Self {
|
fn from((index_style, coordinates): (IndexStyle, (usize, MailboxHash))) -> Self {
|
||||||
match index_style {
|
match index_style {
|
||||||
IndexStyle::Plain => Plain(PlainListing::new(coordinates)),
|
IndexStyle::Plain => Plain(PlainListing::new(coordinates)),
|
||||||
IndexStyle::Threaded => Threaded(ThreadListing::new(coordinates)),
|
IndexStyle::Threaded => Threaded(ThreadListing::new(coordinates)),
|
||||||
|
@ -909,10 +909,10 @@ impl Listing {
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, a)| {
|
.map(|(i, a)| {
|
||||||
let entries: SmallVec<[(usize, FolderHash); 16]> = a
|
let entries: SmallVec<[(usize, MailboxHash); 16]> = a
|
||||||
.list_folders()
|
.list_mailboxes()
|
||||||
.into_iter()
|
.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))
|
.map(|f| (f.depth, f.hash))
|
||||||
.collect::<_>();
|
.collect::<_>();
|
||||||
|
|
||||||
|
@ -981,10 +981,10 @@ impl Listing {
|
||||||
debug!("BUG: invalid area in print_account");
|
debug!("BUG: invalid area in print_account");
|
||||||
}
|
}
|
||||||
// Each entry and its index in the account
|
// Each entry and its index in the account
|
||||||
let folders: FnvHashMap<FolderHash, Folder> = context.accounts[a.index]
|
let mailboxes: FnvHashMap<MailboxHash, Mailbox> = context.accounts[a.index]
|
||||||
.folder_entries
|
.mailbox_entries
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(&hash, entry)| (hash, entry.ref_folder.clone()))
|
.map(|(&hash, entry)| (hash, entry.ref_mailbox.clone()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let upper_left = upper_left!(area);
|
let upper_left = upper_left!(area);
|
||||||
|
@ -992,21 +992,21 @@ impl Listing {
|
||||||
|
|
||||||
let must_highlight_account: bool = self.cursor_pos.0 == a.index;
|
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() {
|
for (i, &(depth, mailbox_hash)) in a.entries.iter().enumerate() {
|
||||||
if folders[&folder_hash].is_subscribed() {
|
if mailboxes[&mailbox_hash].is_subscribed() {
|
||||||
match context.accounts[a.index].status(folder_hash) {
|
match context.accounts[a.index].status(mailbox_hash) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
lines.push((
|
lines.push((
|
||||||
depth,
|
depth,
|
||||||
i,
|
i,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
folders[&folder_hash].count().ok().map(|(v, _)| v),
|
mailboxes[&mailbox_hash].count().ok().map(|(v, _)| v),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Err(_) => {
|
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];
|
let (depth, inc, mailbox_idx, count) = lines[idx];
|
||||||
/* Calculate how many columns the folder index tags should occupy with right alignment,
|
/* Calculate how many columns the mailbox index tags should occupy with right alignment,
|
||||||
* eg.
|
* eg.
|
||||||
* 1
|
* 1
|
||||||
* 2
|
* 2
|
||||||
|
@ -1084,7 +1084,7 @@ impl Listing {
|
||||||
* 9
|
* 9
|
||||||
* 10
|
* 10
|
||||||
*/
|
*/
|
||||||
let total_folder_no_digits = {
|
let total_mailbox_no_digits = {
|
||||||
let mut len = lines_len;
|
let mut len = lines_len;
|
||||||
let mut ctr = 1;
|
let mut ctr = 1;
|
||||||
while len > 9 {
|
while len > 9 {
|
||||||
|
@ -1094,7 +1094,7 @@ impl Listing {
|
||||||
ctr
|
ctr
|
||||||
};
|
};
|
||||||
let (x, _) = write_string_to_grid(
|
let (x, _) = write_string_to_grid(
|
||||||
&format!("{:>width$}", inc, width = total_folder_no_digits),
|
&format!("{:>width$}", inc, width = total_mailbox_no_digits),
|
||||||
grid,
|
grid,
|
||||||
index_att.fg,
|
index_att.fg,
|
||||||
index_att.bg,
|
index_att.bg,
|
||||||
|
@ -1112,7 +1112,7 @@ impl Listing {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
let (x, _) = write_string_to_grid(
|
let (x, _) = write_string_to_grid(
|
||||||
folders[&folder_idx].name(),
|
mailboxes[&mailbox_idx].name(),
|
||||||
grid,
|
grid,
|
||||||
att.fg,
|
att.fg,
|
||||||
att.bg,
|
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())),
|
std::cmp::min(x, get_x(bottom_right).saturating_sub(count_string.len())),
|
||||||
y,
|
y,
|
||||||
),
|
),
|
||||||
|
@ -1167,27 +1167,27 @@ impl Listing {
|
||||||
|
|
||||||
fn change_account(&mut self, context: &mut Context) {
|
fn change_account(&mut self, context: &mut Context) {
|
||||||
self.accounts[self.cursor_pos.0].entries = context.accounts[self.cursor_pos.0]
|
self.accounts[self.cursor_pos.0].entries = context.accounts[self.cursor_pos.0]
|
||||||
.list_folders()
|
.list_mailboxes()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|folder_node| {
|
.filter(|mailbox_node| {
|
||||||
context.accounts[self.cursor_pos.0][&folder_node.hash]
|
context.accounts[self.cursor_pos.0][&mailbox_node.hash]
|
||||||
.ref_folder
|
.ref_mailbox
|
||||||
.is_subscribed()
|
.is_subscribed()
|
||||||
})
|
})
|
||||||
.map(|f| (f.depth, f.hash))
|
.map(|f| (f.depth, f.hash))
|
||||||
.collect::<_>();
|
.collect::<_>();
|
||||||
/* Account might have no folders yet if it's offline */
|
/* Account might have no mailboxes yet if it's offline */
|
||||||
if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0]
|
if let Some((_, mailbox_hash)) = self.accounts[self.cursor_pos.0]
|
||||||
.entries
|
.entries
|
||||||
.get(self.cursor_pos.1)
|
.get(self.cursor_pos.1)
|
||||||
{
|
{
|
||||||
self.component
|
self.component
|
||||||
.set_coordinates((self.cursor_pos.0, *folder_hash));
|
.set_coordinates((self.cursor_pos.0, *mailbox_hash));
|
||||||
/* Check if per-folder configuration overrides general configuration */
|
/* Check if per-mailbox configuration overrides general configuration */
|
||||||
if let Some(index_style) = context
|
if let Some(index_style) = context
|
||||||
.accounts
|
.accounts
|
||||||
.get(self.cursor_pos.0)
|
.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);
|
self.component.set_style(index_style);
|
||||||
} else if let Some(index_style) = context
|
} else if let Some(index_style) = context
|
||||||
|
|
|
@ -48,9 +48,9 @@ macro_rules! address_list {
|
||||||
/// `ThreadView`.
|
/// `ThreadView`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CompactListing {
|
pub struct CompactListing {
|
||||||
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
|
/// (x, y, z): x is accounts, y is mailboxes, z is index inside a mailbox.
|
||||||
cursor_pos: (usize, FolderHash, usize),
|
cursor_pos: (usize, MailboxHash, usize),
|
||||||
new_cursor_pos: (usize, FolderHash, usize),
|
new_cursor_pos: (usize, MailboxHash, usize),
|
||||||
length: usize,
|
length: usize,
|
||||||
sort: (SortField, SortOrder),
|
sort: (SortField, SortOrder),
|
||||||
subsort: (SortField, SortOrder),
|
subsort: (SortField, SortOrder),
|
||||||
|
@ -100,7 +100,7 @@ impl MailListingTrait for CompactListing {
|
||||||
SmallVec::from_iter(iter.into_iter())
|
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.
|
/// chosen.
|
||||||
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
|
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
@ -184,11 +184,11 @@ impl MailListingTrait for CompactListing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ListingTrait 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)
|
(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;
|
self.unfocused = false;
|
||||||
self.view = ThreadView::default();
|
self.view = ThreadView::default();
|
||||||
|
@ -618,7 +618,7 @@ impl fmt::Display for CompactListing {
|
||||||
|
|
||||||
impl CompactListing {
|
impl CompactListing {
|
||||||
const DESCRIPTION: &'static str = "compact listing";
|
const DESCRIPTION: &'static str = "compact listing";
|
||||||
pub fn new(coordinates: (usize, FolderHash)) -> Self {
|
pub fn new(coordinates: (usize, MailboxHash)) -> Self {
|
||||||
CompactListing {
|
CompactListing {
|
||||||
cursor_pos: (0, 1, 0),
|
cursor_pos: (0, 1, 0),
|
||||||
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
||||||
|
@ -650,14 +650,14 @@ impl CompactListing {
|
||||||
hash: ThreadHash,
|
hash: ThreadHash,
|
||||||
) -> EntryStrings {
|
) -> EntryStrings {
|
||||||
let thread = threads.thread_ref(hash);
|
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 tags = String::new();
|
||||||
let mut colors: SmallVec<[_; 8]> = SmallVec::new();
|
let mut colors: SmallVec<[_; 8]> = SmallVec::new();
|
||||||
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
||||||
if let Some(t) = backend_lck.tags() {
|
if let Some(t) = backend_lck.tags() {
|
||||||
let tags_lck = t.read().unwrap();
|
let tags_lck = t.read().unwrap();
|
||||||
for t in e.labels().iter() {
|
for t in e.labels().iter() {
|
||||||
if folder
|
if mailbox
|
||||||
.conf_override
|
.conf_override
|
||||||
.tags
|
.tags
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -669,7 +669,7 @@ impl CompactListing {
|
||||||
tags.push(' ');
|
tags.push(' ');
|
||||||
tags.push_str(tags_lck.get(t).as_ref().unwrap());
|
tags.push_str(tags_lck.get(t).as_ref().unwrap());
|
||||||
tags.push(' ');
|
tags.push(' ');
|
||||||
if let Some(&c) = folder
|
if let Some(&c) = mailbox
|
||||||
.conf_override
|
.conf_override
|
||||||
.tags
|
.tags
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
|
@ -27,9 +27,9 @@ use std::iter::FromIterator;
|
||||||
/// `ThreadView`.
|
/// `ThreadView`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ConversationsListing {
|
pub struct ConversationsListing {
|
||||||
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
|
/// (x, y, z): x is accounts, y is mailboxes, z is index inside a mailbox.
|
||||||
cursor_pos: (usize, FolderHash, usize),
|
cursor_pos: (usize, MailboxHash, usize),
|
||||||
new_cursor_pos: (usize, FolderHash, usize),
|
new_cursor_pos: (usize, MailboxHash, usize),
|
||||||
length: usize,
|
length: usize,
|
||||||
sort: (SortField, SortOrder),
|
sort: (SortField, SortOrder),
|
||||||
subsort: (SortField, SortOrder),
|
subsort: (SortField, SortOrder),
|
||||||
|
@ -79,11 +79,11 @@ impl MailListingTrait for ConversationsListing {
|
||||||
SmallVec::from_iter(iter.into_iter())
|
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.
|
/// chosen.
|
||||||
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
|
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
|
||||||
self.dirty = true;
|
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;
|
let old_cursor_pos = self.cursor_pos;
|
||||||
if !(self.cursor_pos.0 == self.new_cursor_pos.0
|
if !(self.cursor_pos.0 == self.new_cursor_pos.0
|
||||||
&& self.cursor_pos.1 == self.new_cursor_pos.1)
|
&& 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>>,
|
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);
|
self.view.update(context);
|
||||||
} else if self.unfocused {
|
} else if self.unfocused {
|
||||||
let thread_group = self.get_thread_under_cursor(self.cursor_pos.2);
|
let thread_group = self.get_thread_under_cursor(self.cursor_pos.2);
|
||||||
|
@ -170,11 +171,11 @@ impl MailListingTrait for ConversationsListing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ListingTrait 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)
|
(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.new_cursor_pos = (coordinates.0, coordinates.1, 0);
|
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
|
||||||
self.unfocused = false;
|
self.unfocused = false;
|
||||||
|
@ -559,7 +560,7 @@ impl fmt::Display for ConversationsListing {
|
||||||
|
|
||||||
impl ConversationsListing {
|
impl ConversationsListing {
|
||||||
const DESCRIPTION: &'static str = "compact listing";
|
const DESCRIPTION: &'static str = "compact listing";
|
||||||
pub fn new(coordinates: (usize, FolderHash)) -> Self {
|
pub fn new(coordinates: (usize, MailboxHash)) -> Self {
|
||||||
ConversationsListing {
|
ConversationsListing {
|
||||||
cursor_pos: (0, 1, 0),
|
cursor_pos: (0, 1, 0),
|
||||||
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
||||||
|
@ -592,14 +593,14 @@ impl ConversationsListing {
|
||||||
hash: ThreadHash,
|
hash: ThreadHash,
|
||||||
) -> EntryStrings {
|
) -> EntryStrings {
|
||||||
let thread = threads.thread_ref(hash);
|
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 tags = String::new();
|
||||||
let mut colors = SmallVec::new();
|
let mut colors = SmallVec::new();
|
||||||
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
||||||
if let Some(t) = backend_lck.tags() {
|
if let Some(t) = backend_lck.tags() {
|
||||||
let tags_lck = t.read().unwrap();
|
let tags_lck = t.read().unwrap();
|
||||||
for t in e.labels().iter() {
|
for t in e.labels().iter() {
|
||||||
if folder
|
if mailbox
|
||||||
.conf_override
|
.conf_override
|
||||||
.tags
|
.tags
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -611,7 +612,7 @@ impl ConversationsListing {
|
||||||
tags.push(' ');
|
tags.push(' ');
|
||||||
tags.push_str(tags_lck.get(t).as_ref().unwrap());
|
tags.push_str(tags_lck.get(t).as_ref().unwrap());
|
||||||
tags.push(' ');
|
tags.push(' ');
|
||||||
if let Some(&c) = folder
|
if let Some(&c) = mailbox
|
||||||
.conf_override
|
.conf_override
|
||||||
.tags
|
.tags
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
|
@ -24,7 +24,7 @@ use crate::components::utilities::PageMovement;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct OfflineListing {
|
pub struct OfflineListing {
|
||||||
cursor_pos: (usize, FolderHash),
|
cursor_pos: (usize, MailboxHash),
|
||||||
_row_updates: SmallVec<[ThreadHash; 8]>,
|
_row_updates: SmallVec<[ThreadHash; 8]>,
|
||||||
|
|
||||||
id: ComponentId,
|
id: ComponentId,
|
||||||
|
@ -44,11 +44,11 @@ impl MailListingTrait for OfflineListing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ListingTrait for OfflineListing {
|
impl ListingTrait for OfflineListing {
|
||||||
fn coordinates(&self) -> (usize, FolderHash) {
|
fn coordinates(&self) -> (usize, MailboxHash) {
|
||||||
self.cursor_pos
|
self.cursor_pos
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) {
|
fn set_coordinates(&mut self, coordinates: (usize, MailboxHash)) {
|
||||||
self.cursor_pos = coordinates;
|
self.cursor_pos = coordinates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ impl fmt::Display for OfflineListing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OfflineListing {
|
impl OfflineListing {
|
||||||
pub fn new(cursor_pos: (usize, FolderHash)) -> Self {
|
pub fn new(cursor_pos: (usize, MailboxHash)) -> Self {
|
||||||
OfflineListing {
|
OfflineListing {
|
||||||
cursor_pos,
|
cursor_pos,
|
||||||
_row_updates: SmallVec::new(),
|
_row_updates: SmallVec::new(),
|
||||||
|
|
|
@ -47,9 +47,9 @@ macro_rules! address_list {
|
||||||
/// `MailView`.
|
/// `MailView`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PlainListing {
|
pub struct PlainListing {
|
||||||
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
|
/// (x, y, z): x is accounts, y is mailboxes, z is index inside a mailbox.
|
||||||
cursor_pos: (usize, FolderHash, usize),
|
cursor_pos: (usize, MailboxHash, usize),
|
||||||
new_cursor_pos: (usize, FolderHash, usize),
|
new_cursor_pos: (usize, MailboxHash, usize),
|
||||||
length: usize,
|
length: usize,
|
||||||
sort: (SortField, SortOrder),
|
sort: (SortField, SortOrder),
|
||||||
subsort: (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.
|
/// chosen.
|
||||||
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
|
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
@ -188,11 +188,11 @@ impl MailListingTrait for PlainListing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ListingTrait 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)
|
(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;
|
self.unfocused = false;
|
||||||
self.view = MailView::default();
|
self.view = MailView::default();
|
||||||
|
@ -584,7 +584,7 @@ impl fmt::Display for PlainListing {
|
||||||
|
|
||||||
impl PlainListing {
|
impl PlainListing {
|
||||||
const DESCRIPTION: &'static str = "plain listing";
|
const DESCRIPTION: &'static str = "plain listing";
|
||||||
pub fn new(coordinates: (usize, FolderHash)) -> Self {
|
pub fn new(coordinates: (usize, MailboxHash)) -> Self {
|
||||||
PlainListing {
|
PlainListing {
|
||||||
cursor_pos: (0, 1, 0),
|
cursor_pos: (0, 1, 0),
|
||||||
new_cursor_pos: (coordinates.0, coordinates.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 {
|
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 tags = String::new();
|
||||||
let mut colors = SmallVec::new();
|
let mut colors = SmallVec::new();
|
||||||
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
||||||
if let Some(t) = backend_lck.tags() {
|
if let Some(t) = backend_lck.tags() {
|
||||||
let tags_lck = t.read().unwrap();
|
let tags_lck = t.read().unwrap();
|
||||||
for t in e.labels().iter() {
|
for t in e.labels().iter() {
|
||||||
if folder
|
if mailbox
|
||||||
.conf_override
|
.conf_override
|
||||||
.tags
|
.tags
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -632,7 +632,7 @@ impl PlainListing {
|
||||||
tags.push(' ');
|
tags.push(' ');
|
||||||
tags.push_str(tags_lck.get(t).as_ref().unwrap());
|
tags.push_str(tags_lck.get(t).as_ref().unwrap());
|
||||||
tags.push(' ');
|
tags.push(' ');
|
||||||
if let Some(&c) = folder
|
if let Some(&c) = mailbox
|
||||||
.conf_override
|
.conf_override
|
||||||
.tags
|
.tags
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
|
|
@ -28,9 +28,9 @@ const MAX_COLS: usize = 500;
|
||||||
/// `MailView`.
|
/// `MailView`.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ThreadListing {
|
pub struct ThreadListing {
|
||||||
/// (x, y, z): x is accounts, y is folders, z is index inside a folder.
|
/// (x, y, z): x is accounts, y is mailboxes, z is index inside a mailbox.
|
||||||
cursor_pos: (usize, FolderHash, usize),
|
cursor_pos: (usize, MailboxHash, usize),
|
||||||
new_cursor_pos: (usize, FolderHash, usize),
|
new_cursor_pos: (usize, MailboxHash, usize),
|
||||||
length: usize,
|
length: usize,
|
||||||
sort: (SortField, SortOrder),
|
sort: (SortField, SortOrder),
|
||||||
subsort: (SortField, SortOrder),
|
subsort: (SortField, SortOrder),
|
||||||
|
@ -59,7 +59,7 @@ impl MailListingTrait for ThreadListing {
|
||||||
SmallVec::new()
|
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.
|
/// chosen.
|
||||||
fn refresh_mailbox(&mut self, context: &mut Context, _force: bool) {
|
fn refresh_mailbox(&mut self, context: &mut Context, _force: bool) {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
|
@ -127,7 +127,7 @@ impl MailListingTrait for ThreadListing {
|
||||||
ret
|
ret
|
||||||
};
|
};
|
||||||
if threads.len() == 0 {
|
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);
|
self.content = CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
&message,
|
&message,
|
||||||
|
@ -226,10 +226,10 @@ impl MailListingTrait for ThreadListing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ListingTrait 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)
|
(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;
|
self.unfocused = false;
|
||||||
self.view = None;
|
self.view = None;
|
||||||
|
@ -396,7 +396,7 @@ impl fmt::Display for ThreadListing {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ThreadListing {
|
impl ThreadListing {
|
||||||
pub fn new(coordinates: (usize, FolderHash)) -> Self {
|
pub fn new(coordinates: (usize, MailboxHash)) -> Self {
|
||||||
ThreadListing {
|
ThreadListing {
|
||||||
cursor_pos: (0, 1, 0),
|
cursor_pos: (0, 1, 0),
|
||||||
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
||||||
|
|
|
@ -308,9 +308,9 @@ impl StatusPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let count = a
|
let count = a
|
||||||
.folder_entries
|
.mailbox_entries
|
||||||
.values()
|
.values()
|
||||||
.map(|entry| &entry.ref_folder)
|
.map(|entry| &entry.ref_mailbox)
|
||||||
.fold((0, 0), |acc, f| {
|
.fold((0, 0), |acc, f| {
|
||||||
let count = f.count().unwrap_or((0, 0));
|
let count = f.count().unwrap_or((0, 0));
|
||||||
(acc.0 + count.0, acc.1 + count.1)
|
(acc.0 + count.0, acc.1 + count.1)
|
||||||
|
@ -361,9 +361,9 @@ impl StatusPanel {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
for (i, f) in a
|
for (i, f) in a
|
||||||
.folder_entries
|
.mailbox_entries
|
||||||
.values()
|
.values()
|
||||||
.map(|entry| &entry.ref_folder)
|
.map(|entry| &entry.ref_mailbox)
|
||||||
.filter(|f| f.special_usage() != SpecialUsageMailbox::Normal)
|
.filter(|f| f.special_usage() != SpecialUsageMailbox::Normal)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
|
@ -479,9 +479,9 @@ impl Component for AccountStatus {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
for f in a
|
for f in a
|
||||||
.folder_entries
|
.mailbox_entries
|
||||||
.values()
|
.values()
|
||||||
.map(|entry| &entry.ref_folder)
|
.map(|entry| &entry.ref_mailbox)
|
||||||
.filter(|f| f.special_usage() != SpecialUsageMailbox::Normal)
|
.filter(|f| f.special_usage() != SpecialUsageMailbox::Normal)
|
||||||
{
|
{
|
||||||
line += 1;
|
line += 1;
|
||||||
|
@ -497,7 +497,7 @@ impl Component for AccountStatus {
|
||||||
}
|
}
|
||||||
line += 2;
|
line += 2;
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
"Subscribed folders:",
|
"Subscribed mailboxes:",
|
||||||
&mut self.content,
|
&mut self.content,
|
||||||
self.theme_default.fg,
|
self.theme_default.fg,
|
||||||
self.theme_default.bg,
|
self.theme_default.bg,
|
||||||
|
@ -506,8 +506,8 @@ impl Component for AccountStatus {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
line += 2;
|
line += 2;
|
||||||
for folder_node in a.list_folders() {
|
for mailbox_node in a.list_mailboxes() {
|
||||||
let f: &Folder = &a[&folder_node.hash].ref_folder;
|
let f: &Mailbox = &a[&mailbox_node.hash].ref_mailbox;
|
||||||
if f.is_subscribed() {
|
if f.is_subscribed() {
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
f.path(),
|
f.path(),
|
||||||
|
|
|
@ -86,7 +86,7 @@ impl ViewMode {
|
||||||
/// menus
|
/// menus
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct MailView {
|
pub struct MailView {
|
||||||
coordinates: (usize, FolderHash, EnvelopeHash),
|
coordinates: (usize, MailboxHash, EnvelopeHash),
|
||||||
pager: Pager,
|
pager: Pager,
|
||||||
subview: Option<Box<dyn Component>>,
|
subview: Option<Box<dyn Component>>,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
|
@ -124,7 +124,7 @@ impl fmt::Display for MailView {
|
||||||
impl MailView {
|
impl MailView {
|
||||||
const DESCRIPTION: &'static str = "view mail";
|
const DESCRIPTION: &'static str = "view mail";
|
||||||
pub fn new(
|
pub fn new(
|
||||||
coordinates: (usize, FolderHash, EnvelopeHash),
|
coordinates: (usize, MailboxHash, EnvelopeHash),
|
||||||
pager: Option<Pager>,
|
pager: Option<Pager>,
|
||||||
subview: Option<Box<dyn Component>>,
|
subview: Option<Box<dyn Component>>,
|
||||||
context: &Context,
|
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.coordinates = new_coordinates;
|
||||||
self.mode = ViewMode::Normal;
|
self.mode = ViewMode::Normal;
|
||||||
self.initialised = false;
|
self.initialised = false;
|
||||||
|
|
|
@ -51,7 +51,7 @@ pub struct ThreadView {
|
||||||
expanded_pos: usize,
|
expanded_pos: usize,
|
||||||
new_expanded_pos: usize,
|
new_expanded_pos: usize,
|
||||||
reversed: bool,
|
reversed: bool,
|
||||||
coordinates: (usize, FolderHash, usize),
|
coordinates: (usize, MailboxHash, usize),
|
||||||
thread_group: ThreadHash,
|
thread_group: ThreadHash,
|
||||||
mailview: MailView,
|
mailview: MailView,
|
||||||
show_mailview: bool,
|
show_mailview: bool,
|
||||||
|
@ -69,13 +69,13 @@ pub struct ThreadView {
|
||||||
impl ThreadView {
|
impl ThreadView {
|
||||||
const DESCRIPTION: &'static str = "thread view";
|
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_hash: optional position of expanded entry when we render the threadview. Default
|
||||||
* expanded message is the last one.
|
* expanded message is the last one.
|
||||||
* context: current context
|
* context: current context
|
||||||
*/
|
*/
|
||||||
pub fn new(
|
pub fn new(
|
||||||
coordinates: (usize, FolderHash, usize),
|
coordinates: (usize, MailboxHash, usize),
|
||||||
thread_group: ThreadHash,
|
thread_group: ThreadHash,
|
||||||
expanded_hash: Option<ThreadNodeHash>,
|
expanded_hash: Option<ThreadNodeHash>,
|
||||||
context: &Context,
|
context: &Context,
|
||||||
|
|
64
src/conf.rs
64
src/conf.rs
|
@ -51,7 +51,7 @@ use self::notifications::NotificationsSettings;
|
||||||
use self::terminal::TerminalSettings;
|
use self::terminal::TerminalSettings;
|
||||||
use crate::pager::PagerSettings;
|
use crate::pager::PagerSettings;
|
||||||
use crate::plugins::Plugin;
|
use crate::plugins::Plugin;
|
||||||
use melib::conf::{AccountSettings, FolderConf, ToggleFlag};
|
use melib::conf::{AccountSettings, MailboxConf, ToggleFlag};
|
||||||
use melib::error::*;
|
use melib::error::*;
|
||||||
|
|
||||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
@ -85,27 +85,27 @@ pub struct MailUIConf {
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||||
pub struct FileFolderConf {
|
pub struct FileMailboxConf {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub conf_override: MailUIConf,
|
pub conf_override: MailUIConf,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub folder_conf: FolderConf,
|
pub mailbox_conf: MailboxConf,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileFolderConf {
|
impl FileMailboxConf {
|
||||||
pub fn conf_override(&self) -> &MailUIConf {
|
pub fn conf_override(&self) -> &MailUIConf {
|
||||||
&self.conf_override
|
&self.conf_override
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn folder_conf(&self) -> &FolderConf {
|
pub fn mailbox_conf(&self) -> &MailboxConf {
|
||||||
&self.folder_conf
|
&self.mailbox_conf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use crate::conf::deserializers::extra_settings;
|
use crate::conf::deserializers::extra_settings;
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
pub struct FileAccount {
|
pub struct FileAccount {
|
||||||
root_folder: String,
|
root_mailbox: String,
|
||||||
format: String,
|
format: String,
|
||||||
identity: String,
|
identity: String,
|
||||||
#[serde(default = "none")]
|
#[serde(default = "none")]
|
||||||
|
@ -115,9 +115,9 @@ pub struct FileAccount {
|
||||||
#[serde(default = "false_val")]
|
#[serde(default = "false_val")]
|
||||||
read_only: bool,
|
read_only: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
subscribed_folders: Vec<String>,
|
subscribed_mailboxes: Vec<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
folders: HashMap<String, FileFolderConf>,
|
mailboxes: HashMap<String, FileMailboxConf>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
cache_type: CacheType,
|
cache_type: CacheType,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
@ -132,44 +132,44 @@ pub struct FileAccount {
|
||||||
impl From<FileAccount> for AccountConf {
|
impl From<FileAccount> for AccountConf {
|
||||||
fn from(x: FileAccount) -> Self {
|
fn from(x: FileAccount) -> Self {
|
||||||
let format = x.format.to_lowercase();
|
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 identity = x.identity.clone();
|
||||||
let display_name = x.display_name.clone();
|
let display_name = x.display_name.clone();
|
||||||
let folders = x
|
let mailboxes = x
|
||||||
.folders
|
.mailboxes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, v)| (k.clone(), v.folder_conf.clone()))
|
.map(|(k, v)| (k.clone(), v.mailbox_conf.clone()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let acc = AccountSettings {
|
let acc = AccountSettings {
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
root_folder,
|
root_mailbox,
|
||||||
format,
|
format,
|
||||||
identity,
|
identity,
|
||||||
read_only: x.read_only,
|
read_only: x.read_only,
|
||||||
display_name,
|
display_name,
|
||||||
subscribed_folders: x.subscribed_folders.clone(),
|
subscribed_mailboxes: x.subscribed_mailboxes.clone(),
|
||||||
folders,
|
mailboxes,
|
||||||
manual_refresh: x.manual_refresh,
|
manual_refresh: x.manual_refresh,
|
||||||
extra: x.extra.clone(),
|
extra: x.extra.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let folder_confs = x.folders.clone();
|
let mailbox_confs = x.mailboxes.clone();
|
||||||
AccountConf {
|
AccountConf {
|
||||||
account: acc,
|
account: acc,
|
||||||
conf: x,
|
conf: x,
|
||||||
folder_confs,
|
mailbox_confs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileAccount {
|
impl FileAccount {
|
||||||
pub fn folders(&self) -> &HashMap<String, FileFolderConf> {
|
pub fn mailboxes(&self) -> &HashMap<String, FileMailboxConf> {
|
||||||
&self.folders
|
&self.mailboxes
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn folder(&self) -> &str {
|
pub fn mailbox(&self) -> &str {
|
||||||
&self.root_folder
|
&self.root_mailbox
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn index_style(&self) -> IndexStyle {
|
pub fn index_style(&self) -> IndexStyle {
|
||||||
|
@ -207,7 +207,7 @@ pub struct FileSettings {
|
||||||
pub struct AccountConf {
|
pub struct AccountConf {
|
||||||
pub(crate) account: AccountSettings,
|
pub(crate) account: AccountSettings,
|
||||||
pub(crate) conf: FileAccount,
|
pub(crate) conf: FileAccount,
|
||||||
pub(crate) folder_confs: HashMap<String, FileFolderConf>,
|
pub(crate) mailbox_confs: HashMap<String, FileMailboxConf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AccountConf {
|
impl AccountConf {
|
||||||
|
@ -342,13 +342,13 @@ impl FileSettings {
|
||||||
s.terminal.themes.validate()?;
|
s.terminal.themes.validate()?;
|
||||||
for (name, acc) in &s.accounts {
|
for (name, acc) in &s.accounts {
|
||||||
let FileAccount {
|
let FileAccount {
|
||||||
root_folder,
|
root_mailbox,
|
||||||
format,
|
format,
|
||||||
identity,
|
identity,
|
||||||
read_only,
|
read_only,
|
||||||
display_name,
|
display_name,
|
||||||
subscribed_folders,
|
subscribed_mailboxes,
|
||||||
folders,
|
mailboxes,
|
||||||
extra,
|
extra,
|
||||||
manual_refresh,
|
manual_refresh,
|
||||||
refresh_command: _,
|
refresh_command: _,
|
||||||
|
@ -359,16 +359,16 @@ impl FileSettings {
|
||||||
let lowercase_format = format.to_lowercase();
|
let lowercase_format = format.to_lowercase();
|
||||||
let s = AccountSettings {
|
let s = AccountSettings {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
root_folder,
|
root_mailbox,
|
||||||
format: format.clone(),
|
format: format.clone(),
|
||||||
identity,
|
identity,
|
||||||
read_only,
|
read_only,
|
||||||
display_name,
|
display_name,
|
||||||
subscribed_folders,
|
subscribed_mailboxes,
|
||||||
manual_refresh,
|
manual_refresh,
|
||||||
folders: folders
|
mailboxes: mailboxes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, v)| (k, v.folder_conf))
|
.map(|(k, v)| (k, v.mailbox_conf))
|
||||||
.collect(),
|
.collect(),
|
||||||
extra,
|
extra,
|
||||||
};
|
};
|
||||||
|
@ -714,8 +714,8 @@ mod pp {
|
||||||
let mut ret = pp_helper(&p_buf, 0)?;
|
let mut ret = pp_helper(&p_buf, 0)?;
|
||||||
drop(p_buf);
|
drop(p_buf);
|
||||||
if let Ok(xdg_dirs) = xdg::BaseDirectories::with_prefix("meli") {
|
if let Ok(xdg_dirs) = xdg::BaseDirectories::with_prefix("meli") {
|
||||||
for theme_folder in xdg_dirs.find_config_files("themes") {
|
for theme_mailbox in xdg_dirs.find_config_files("themes") {
|
||||||
let read_dir = std::fs::read_dir(theme_folder)?;
|
let read_dir = std::fs::read_dir(theme_mailbox)?;
|
||||||
for theme in read_dir {
|
for theme in read_dir {
|
||||||
ret.extend(pp_helper(&theme?.path(), 0)?.chars());
|
ret.extend(pp_helper(&theme?.path(), 0)?.chars());
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,11 @@
|
||||||
* Account management from user configuration.
|
* Account management from user configuration.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::{AccountConf, FileFolderConf};
|
use super::{AccountConf, FileMailboxConf};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use melib::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
use melib::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
||||||
use melib::backends::{
|
use melib::backends::{
|
||||||
BackendOp, Backends, Folder, FolderHash, MailBackend, NotifyFn, ReadOnlyOp, RefreshEvent,
|
BackendOp, Backends, MailBackend, Mailbox, MailboxHash, NotifyFn, ReadOnlyOp, RefreshEvent,
|
||||||
RefreshEventConsumer, RefreshEventKind, SpecialUsageMailbox,
|
RefreshEventConsumer, RefreshEventKind, SpecialUsageMailbox,
|
||||||
};
|
};
|
||||||
use melib::email::*;
|
use melib::email::*;
|
||||||
|
@ -84,15 +84,15 @@ impl MailboxStatus {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FolderEntry {
|
pub struct MailboxEntry {
|
||||||
pub status: MailboxStatus,
|
pub status: MailboxStatus,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub ref_folder: Folder,
|
pub ref_mailbox: Mailbox,
|
||||||
pub conf: FileFolderConf,
|
pub conf: FileMailboxConf,
|
||||||
pub worker: Worker,
|
pub worker: Worker,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FolderEntry {
|
impl MailboxEntry {
|
||||||
pub fn status(&self) -> String {
|
pub fn status(&self) -> String {
|
||||||
match self.status {
|
match self.status {
|
||||||
MailboxStatus::Available => self.name().to_string(),
|
MailboxStatus::Available => self.name().to_string(),
|
||||||
|
@ -114,10 +114,10 @@ pub struct Account {
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
name: String,
|
name: String,
|
||||||
pub is_online: bool,
|
pub is_online: bool,
|
||||||
pub(crate) folder_entries: FnvHashMap<FolderHash, FolderEntry>,
|
pub(crate) mailbox_entries: FnvHashMap<MailboxHash, MailboxEntry>,
|
||||||
pub(crate) folders_order: Vec<FolderHash>,
|
pub(crate) mailboxes_order: Vec<MailboxHash>,
|
||||||
tree: Vec<FolderNode>,
|
tree: Vec<MailboxNode>,
|
||||||
sent_folder: Option<FolderHash>,
|
sent_mailbox: Option<MailboxHash>,
|
||||||
pub(crate) collection: Collection,
|
pub(crate) collection: Collection,
|
||||||
pub(crate) address_book: AddressBook,
|
pub(crate) address_book: AddressBook,
|
||||||
pub(crate) work_context: WorkContext,
|
pub(crate) work_context: WorkContext,
|
||||||
|
@ -126,7 +126,7 @@ pub struct Account {
|
||||||
pub(crate) backend: Arc<RwLock<Box<dyn MailBackend>>>,
|
pub(crate) backend: Arc<RwLock<Box<dyn MailBackend>>>,
|
||||||
|
|
||||||
sender: Sender<ThreadEvent>,
|
sender: Sender<ThreadEvent>,
|
||||||
event_queue: VecDeque<(FolderHash, RefreshEvent)>,
|
event_queue: VecDeque<(MailboxHash, RefreshEvent)>,
|
||||||
notify_fn: Arc<NotifyFn>,
|
notify_fn: Arc<NotifyFn>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,10 +177,10 @@ impl Drop for Account {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug, Clone, Default)]
|
#[derive(Serialize, Debug, Clone, Default)]
|
||||||
pub struct FolderNode {
|
pub struct MailboxNode {
|
||||||
pub hash: FolderHash,
|
pub hash: MailboxHash,
|
||||||
pub depth: usize,
|
pub depth: usize,
|
||||||
pub children: Vec<FolderNode>,
|
pub children: Vec<MailboxNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Account {
|
impl Account {
|
||||||
|
@ -197,11 +197,11 @@ impl Account {
|
||||||
let backend = map.get(settings.account().format())(
|
let backend = map.get(settings.account().format())(
|
||||||
settings.account(),
|
settings.account(),
|
||||||
Box::new(move |path: &str| {
|
Box::new(move |path: &str| {
|
||||||
s.account.subscribed_folders.is_empty()
|
s.account.subscribed_mailboxes.is_empty()
|
||||||
|| (s.folder_confs.contains_key(path)
|
|| (s.mailbox_confs.contains_key(path)
|
||||||
&& s.folder_confs[path].folder_conf().subscribe.is_true())
|
&& s.mailbox_confs[path].mailbox_conf().subscribe.is_true())
|
||||||
|| s.account
|
|| s.account
|
||||||
.subscribed_folders
|
.subscribed_mailboxes
|
||||||
.iter()
|
.iter()
|
||||||
.any(|m| path.matches_glob(m))
|
.any(|m| path.matches_glob(m))
|
||||||
}),
|
}),
|
||||||
|
@ -233,11 +233,11 @@ impl Account {
|
||||||
index,
|
index,
|
||||||
name,
|
name,
|
||||||
is_online: false,
|
is_online: false,
|
||||||
folder_entries: Default::default(),
|
mailbox_entries: Default::default(),
|
||||||
folders_order: Default::default(),
|
mailboxes_order: Default::default(),
|
||||||
tree: Default::default(),
|
tree: Default::default(),
|
||||||
address_book,
|
address_book,
|
||||||
sent_folder: Default::default(),
|
sent_mailbox: Default::default(),
|
||||||
collection: Default::default(),
|
collection: Default::default(),
|
||||||
work_context,
|
work_context,
|
||||||
runtime_settings: settings.clone(),
|
runtime_settings: settings.clone(),
|
||||||
|
@ -251,22 +251,22 @@ impl Account {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(&mut self) {
|
fn init(&mut self) {
|
||||||
let mut ref_folders: FnvHashMap<FolderHash, Folder> =
|
let mut ref_mailboxes: FnvHashMap<MailboxHash, Mailbox> =
|
||||||
match self.backend.read().unwrap().folders() {
|
match self.backend.read().unwrap().mailboxes() {
|
||||||
Ok(f) => f,
|
Ok(f) => f,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
debug!(&err);
|
debug!(&err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut folder_entries: FnvHashMap<FolderHash, FolderEntry> =
|
let mut mailbox_entries: FnvHashMap<MailboxHash, MailboxEntry> =
|
||||||
FnvHashMap::with_capacity_and_hasher(ref_folders.len(), Default::default());
|
FnvHashMap::with_capacity_and_hasher(ref_mailboxes.len(), Default::default());
|
||||||
let mut folders_order: Vec<FolderHash> = Vec::with_capacity(ref_folders.len());
|
let mut mailboxes_order: Vec<MailboxHash> = Vec::with_capacity(ref_mailboxes.len());
|
||||||
|
|
||||||
let mut sent_folder = None;
|
let mut sent_mailbox = None;
|
||||||
for f in ref_folders.values_mut() {
|
for f in ref_mailboxes.values_mut() {
|
||||||
if let Some(conf) = self.settings.folder_confs.get_mut(f.path()) {
|
if let Some(conf) = self.settings.mailbox_confs.get_mut(f.path()) {
|
||||||
conf.folder_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
|
conf.mailbox_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
|
||||||
Some(f.special_usage())
|
Some(f.special_usage())
|
||||||
} else {
|
} else {
|
||||||
let tmp = SpecialUsageMailbox::detect_usage(f.name());
|
let tmp = SpecialUsageMailbox::detect_usage(f.name());
|
||||||
|
@ -275,21 +275,21 @@ impl Account {
|
||||||
}
|
}
|
||||||
tmp
|
tmp
|
||||||
};
|
};
|
||||||
match conf.folder_conf.usage {
|
match conf.mailbox_conf.usage {
|
||||||
Some(SpecialUsageMailbox::Sent) => {
|
Some(SpecialUsageMailbox::Sent) => {
|
||||||
sent_folder = Some(f.hash());
|
sent_mailbox = Some(f.hash());
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
if f.special_usage() == SpecialUsageMailbox::Sent {
|
if f.special_usage() == SpecialUsageMailbox::Sent {
|
||||||
sent_folder = Some(f.hash());
|
sent_mailbox = Some(f.hash());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
folder_entries.insert(
|
mailbox_entries.insert(
|
||||||
f.hash(),
|
f.hash(),
|
||||||
FolderEntry {
|
MailboxEntry {
|
||||||
ref_folder: f.clone(),
|
ref_mailbox: f.clone(),
|
||||||
name: f.path().to_string(),
|
name: f.path().to_string(),
|
||||||
status: MailboxStatus::None,
|
status: MailboxStatus::None,
|
||||||
conf: conf.clone(),
|
conf: conf.clone(),
|
||||||
|
@ -297,8 +297,8 @@ impl Account {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let mut new = FileFolderConf::default();
|
let mut new = FileMailboxConf::default();
|
||||||
new.folder_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
|
new.mailbox_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
|
||||||
Some(f.special_usage())
|
Some(f.special_usage())
|
||||||
} else {
|
} else {
|
||||||
let tmp = SpecialUsageMailbox::detect_usage(f.name());
|
let tmp = SpecialUsageMailbox::detect_usage(f.name());
|
||||||
|
@ -307,14 +307,14 @@ impl Account {
|
||||||
}
|
}
|
||||||
tmp
|
tmp
|
||||||
};
|
};
|
||||||
if new.folder_conf.usage == Some(SpecialUsageMailbox::Sent) {
|
if new.mailbox_conf.usage == Some(SpecialUsageMailbox::Sent) {
|
||||||
sent_folder = Some(f.hash());
|
sent_mailbox = Some(f.hash());
|
||||||
}
|
}
|
||||||
|
|
||||||
folder_entries.insert(
|
mailbox_entries.insert(
|
||||||
f.hash(),
|
f.hash(),
|
||||||
FolderEntry {
|
MailboxEntry {
|
||||||
ref_folder: f.clone(),
|
ref_mailbox: f.clone(),
|
||||||
name: f.path().to_string(),
|
name: f.path().to_string(),
|
||||||
status: MailboxStatus::None,
|
status: MailboxStatus::None,
|
||||||
conf: new,
|
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());
|
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() {
|
if !f.is_subscribed() {
|
||||||
/* Skip unsubscribed folder */
|
/* Skip unsubscribed mailbox */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
folder_entries.entry(*h).and_modify(|entry| {
|
mailbox_entries.entry(*h).and_modify(|entry| {
|
||||||
entry.status = MailboxStatus::Parsing(0, 0);
|
entry.status = MailboxStatus::Parsing(0, 0);
|
||||||
entry.worker = Account::new_worker(
|
entry.worker = Account::new_worker(
|
||||||
f.clone(),
|
f.clone(),
|
||||||
|
@ -344,32 +344,32 @@ impl Account {
|
||||||
collection.threads.insert(*h, Threads::default());
|
collection.threads.insert(*h, Threads::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
build_folders_order(&mut tree, &folder_entries, &mut folders_order);
|
build_mailboxes_order(&mut tree, &mailbox_entries, &mut mailboxes_order);
|
||||||
self.folders_order = folders_order;
|
self.mailboxes_order = mailboxes_order;
|
||||||
self.folder_entries = folder_entries;
|
self.mailbox_entries = mailbox_entries;
|
||||||
self.tree = tree;
|
self.tree = tree;
|
||||||
self.sent_folder = sent_folder;
|
self.sent_mailbox = sent_mailbox;
|
||||||
self.collection = collection;
|
self.collection = collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_worker(
|
fn new_worker(
|
||||||
folder: Folder,
|
mailbox: Mailbox,
|
||||||
backend: &Arc<RwLock<Box<dyn MailBackend>>>,
|
backend: &Arc<RwLock<Box<dyn MailBackend>>>,
|
||||||
work_context: &WorkContext,
|
work_context: &WorkContext,
|
||||||
notify_fn: Arc<NotifyFn>,
|
notify_fn: Arc<NotifyFn>,
|
||||||
) -> Worker {
|
) -> 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 mut builder = AsyncBuilder::new();
|
||||||
let our_tx = builder.tx();
|
let our_tx = builder.tx();
|
||||||
let folder_hash = folder.hash();
|
let mailbox_hash = mailbox.hash();
|
||||||
let priority = match folder.special_usage() {
|
let priority = match mailbox.special_usage() {
|
||||||
SpecialUsageMailbox::Inbox => 0,
|
SpecialUsageMailbox::Inbox => 0,
|
||||||
SpecialUsageMailbox::Sent => 1,
|
SpecialUsageMailbox::Sent => 1,
|
||||||
SpecialUsageMailbox::Drafts | SpecialUsageMailbox::Trash => 2,
|
SpecialUsageMailbox::Drafts | SpecialUsageMailbox::Trash => 2,
|
||||||
_ => {
|
_ => {
|
||||||
3 * folder
|
3 * mailbox
|
||||||
.path()
|
.path()
|
||||||
.split(if folder.path().contains('/') {
|
.split(if mailbox.path().contains('/') {
|
||||||
'/'
|
'/'
|
||||||
} else {
|
} else {
|
||||||
'.'
|
'.'
|
||||||
|
@ -385,7 +385,7 @@ impl Account {
|
||||||
*/
|
*/
|
||||||
builder.set_priority(priority).set_is_static(true);
|
builder.set_priority(priority).set_is_static(true);
|
||||||
let mut w = builder.build(Box::new(move |work_context| {
|
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();
|
let work = mailbox_handle.work().unwrap();
|
||||||
work_context.new_work.send(work).unwrap();
|
work_context.new_work.send(work).unwrap();
|
||||||
let thread_id = std::thread::current().id();
|
let thread_id = std::thread::current().id();
|
||||||
|
@ -399,12 +399,12 @@ impl Account {
|
||||||
match debug!(mailbox_handle.poll_block()) {
|
match debug!(mailbox_handle.poll_block()) {
|
||||||
Ok(s @ AsyncStatus::Payload(_)) => {
|
Ok(s @ AsyncStatus::Payload(_)) => {
|
||||||
our_tx.send(s).unwrap();
|
our_tx.send(s).unwrap();
|
||||||
debug!("notifying for {}", folder_hash);
|
debug!("notifying for {}", mailbox_hash);
|
||||||
notify_fn.notify(folder_hash);
|
notify_fn.notify(mailbox_hash);
|
||||||
}
|
}
|
||||||
Ok(s @ AsyncStatus::Finished) => {
|
Ok(s @ AsyncStatus::Finished) => {
|
||||||
our_tx.send(s).unwrap();
|
our_tx.send(s).unwrap();
|
||||||
notify_fn.notify(folder_hash);
|
notify_fn.notify(mailbox_hash);
|
||||||
debug!("exiting");
|
debug!("exiting");
|
||||||
work_context.finished.send(thread_id).unwrap();
|
work_context.finished.send(thread_id).unwrap();
|
||||||
return;
|
return;
|
||||||
|
@ -424,15 +424,15 @@ impl Account {
|
||||||
}
|
}
|
||||||
Some(w)
|
Some(w)
|
||||||
}
|
}
|
||||||
pub fn reload(&mut self, event: RefreshEvent, folder_hash: FolderHash) -> Option<UIEvent> {
|
pub fn reload(&mut self, event: RefreshEvent, mailbox_hash: MailboxHash) -> Option<UIEvent> {
|
||||||
if !self.folder_entries[&folder_hash].status.is_available() {
|
if !self.mailbox_entries[&mailbox_hash].status.is_available() {
|
||||||
self.event_queue.push_back((folder_hash, event));
|
self.event_queue.push_back((mailbox_hash, event));
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let kind = event.kind();
|
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 {
|
match kind {
|
||||||
RefreshEventKind::Update(old_hash, envelope) => {
|
RefreshEventKind::Update(old_hash, envelope) => {
|
||||||
#[cfg(feature = "sqlite3")]
|
#[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));
|
return Some(EnvelopeUpdate(old_hash));
|
||||||
}
|
}
|
||||||
RefreshEventKind::Rename(old_hash, new_hash) => {
|
RefreshEventKind::Rename(old_hash, new_hash) => {
|
||||||
debug!("rename {} to {}", 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")]
|
#[cfg(feature = "sqlite3")]
|
||||||
{
|
{
|
||||||
let envelopes = self.collection.envelopes.read();
|
let envelopes = self.collection.envelopes.read();
|
||||||
|
@ -478,7 +478,7 @@ impl Account {
|
||||||
RefreshEventKind::Create(envelope) => {
|
RefreshEventKind::Create(envelope) => {
|
||||||
let env_hash = envelope.hash();
|
let env_hash = envelope.hash();
|
||||||
if self.collection.contains_key(&env_hash)
|
if self.collection.contains_key(&env_hash)
|
||||||
&& self.collection[&folder_hash].contains(&env_hash)
|
&& self.collection[&mailbox_hash].contains(&env_hash)
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -505,38 +505,30 @@ impl Account {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.collection.insert(*envelope, folder_hash);
|
self.collection.insert(*envelope, mailbox_hash);
|
||||||
if self
|
|
||||||
.sent_folder
|
|
||||||
.as_ref()
|
|
||||||
.map(|h| *h == folder_hash)
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
self.collection.insert_reply(env_hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.folder_entries[&folder_hash]
|
if self.mailbox_entries[&mailbox_hash]
|
||||||
.conf
|
.conf
|
||||||
.folder_conf
|
.mailbox_conf
|
||||||
.ignore
|
.ignore
|
||||||
.is_true()
|
.is_true()
|
||||||
{
|
{
|
||||||
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
|
return Some(UIEvent::MailboxUpdate((self.index, mailbox_hash)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let thread = {
|
let thread = {
|
||||||
let thread_hash = self.collection.get_env(env_hash).thread();
|
let thread_hash = self.collection.get_env(env_hash).thread();
|
||||||
self.collection.threads[&folder_hash]
|
self.collection.threads[&mailbox_hash]
|
||||||
.find_group(self.collection.threads[&folder_hash][&thread_hash].group)
|
.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)
|
.thread_ref(thread)
|
||||||
.snoozed()
|
.snoozed()
|
||||||
{
|
{
|
||||||
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
|
return Some(UIEvent::MailboxUpdate((self.index, mailbox_hash)));
|
||||||
}
|
}
|
||||||
if is_seen || is_draft {
|
if is_seen || is_draft {
|
||||||
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
|
return Some(UIEvent::MailboxUpdate((self.index, mailbox_hash)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Some(Notification(
|
return Some(Notification(
|
||||||
|
@ -545,7 +537,7 @@ impl Account {
|
||||||
"{}\n{} {}",
|
"{}\n{} {}",
|
||||||
subject,
|
subject,
|
||||||
self.name,
|
self.name,
|
||||||
self.folder_entries[&folder_hash].name()
|
self.mailbox_entries[&mailbox_hash].name()
|
||||||
),
|
),
|
||||||
Some(crate::types::NotificationType::NewMail),
|
Some(crate::types::NotificationType::NewMail),
|
||||||
));
|
));
|
||||||
|
@ -567,19 +559,21 @@ impl Account {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.collection.remove(envelope_hash, folder_hash);
|
self.collection.remove(envelope_hash, mailbox_hash);
|
||||||
return Some(EnvelopeRemove(envelope_hash));
|
return Some(EnvelopeRemove(envelope_hash));
|
||||||
}
|
}
|
||||||
RefreshEventKind::Rescan => {
|
RefreshEventKind::Rescan => {
|
||||||
let handle = Account::new_worker(
|
let handle = Account::new_worker(
|
||||||
self.folder_entries[&folder_hash].ref_folder.clone(),
|
self.mailbox_entries[&mailbox_hash].ref_mailbox.clone(),
|
||||||
&mut self.backend,
|
&mut self.backend,
|
||||||
&self.work_context,
|
&self.work_context,
|
||||||
self.notify_fn.clone(),
|
self.notify_fn.clone(),
|
||||||
);
|
);
|
||||||
self.folder_entries.entry(folder_hash).and_modify(|entry| {
|
self.mailbox_entries
|
||||||
entry.worker = handle;
|
.entry(mailbox_hash)
|
||||||
});
|
.and_modify(|entry| {
|
||||||
|
entry.worker = handle;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
RefreshEventKind::Failure(e) => {
|
RefreshEventKind::Failure(e) => {
|
||||||
debug!("RefreshEvent Failure: {}", e.to_string());
|
debug!("RefreshEvent Failure: {}", e.to_string());
|
||||||
|
@ -599,7 +593,7 @@ impl Account {
|
||||||
}
|
}
|
||||||
None
|
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 {
|
if let Some(ref refresh_command) = self.settings.conf().refresh_command {
|
||||||
let parts = crate::split_command!(refresh_command);
|
let parts = crate::split_command!(refresh_command);
|
||||||
let (cmd, args) = (parts[0], &parts[1..]);
|
let (cmd, args) = (parts[0], &parts[1..]);
|
||||||
|
@ -616,7 +610,7 @@ impl Account {
|
||||||
let r = RefreshEventConsumer::new(Box::new(move |r| {
|
let r = RefreshEventConsumer::new(Box::new(move |r| {
|
||||||
sender_.send(ThreadEvent::from(r)).unwrap();
|
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();
|
self.work_context.new_work.send(h.work().unwrap()).unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -661,9 +655,9 @@ impl Account {
|
||||||
self.tree.is_empty()
|
self.tree.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_folders(&self) -> Vec<FolderNode> {
|
pub fn list_mailboxes(&self) -> Vec<MailboxNode> {
|
||||||
let mut ret = Vec::with_capacity(self.folder_entries.len());
|
let mut ret = Vec::with_capacity(self.mailbox_entries.len());
|
||||||
fn rec(node: &FolderNode, ret: &mut Vec<FolderNode>) {
|
fn rec(node: &MailboxNode, ret: &mut Vec<MailboxNode>) {
|
||||||
ret.push(node.clone());
|
ret.push(node.clone());
|
||||||
for c in node.children.iter() {
|
for c in node.children.iter() {
|
||||||
rec(c, ret);
|
rec(c, ret);
|
||||||
|
@ -675,20 +669,22 @@ impl Account {
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn folders_order(&self) -> &Vec<FolderHash> {
|
pub fn mailboxes_order(&self) -> &Vec<MailboxHash> {
|
||||||
&self.folders_order
|
&self.mailboxes_order
|
||||||
}
|
}
|
||||||
pub fn name(&self) -> &str {
|
pub fn name(&self) -> &str {
|
||||||
&self.name
|
&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() {
|
if payload.is_err() {
|
||||||
self.folder_entries.entry(folder_hash).and_modify(|entry| {
|
self.mailbox_entries
|
||||||
entry.status = MailboxStatus::Failed(payload.unwrap_err());
|
.entry(mailbox_hash)
|
||||||
});
|
.and_modify(|entry| {
|
||||||
|
entry.status = MailboxStatus::Failed(payload.unwrap_err());
|
||||||
|
});
|
||||||
self.sender
|
self.sender
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(folder_hash)))
|
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(mailbox_hash)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -697,37 +693,37 @@ impl Account {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|e| (e.hash(), e))
|
.map(|e| (e.hash(), e))
|
||||||
.collect::<FnvHashMap<EnvelopeHash, Envelope>>();
|
.collect::<FnvHashMap<EnvelopeHash, Envelope>>();
|
||||||
if let Some(updated_folders) =
|
if let Some(updated_mailboxes) =
|
||||||
self.collection
|
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
|
self.sender
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(f)))
|
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(f)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.sender
|
self.sender
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(folder_hash)))
|
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(mailbox_hash)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn status(&mut self, folder_hash: FolderHash) -> result::Result<(), usize> {
|
pub fn status(&mut self, mailbox_hash: MailboxHash) -> result::Result<(), usize> {
|
||||||
if folder_hash == 0 {
|
if mailbox_hash == 0 {
|
||||||
return Err(0);
|
return Err(0);
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
match self
|
match self
|
||||||
.folder_entries
|
.mailbox_entries
|
||||||
.get_mut(&folder_hash)
|
.get_mut(&mailbox_hash)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.worker
|
.worker
|
||||||
.as_mut()
|
.as_mut()
|
||||||
{
|
{
|
||||||
None => {
|
None => {
|
||||||
return if self.folder_entries[&folder_hash].status.is_available()
|
return if self.mailbox_entries[&mailbox_hash].status.is_available()
|
||||||
|| (self.folder_entries[&folder_hash].status.is_parsing()
|
|| (self.mailbox_entries[&mailbox_hash].status.is_parsing()
|
||||||
&& self.collection.mailboxes.contains_key(&folder_hash))
|
&& self.collection.mailboxes.contains_key(&mailbox_hash))
|
||||||
{
|
{
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -739,31 +735,33 @@ impl Account {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Ok(AsyncStatus::Payload(envs)) => {
|
Ok(AsyncStatus::Payload(envs)) => {
|
||||||
debug!("got payload in status for {}", folder_hash);
|
debug!("got payload in status for {}", mailbox_hash);
|
||||||
self.load_mailbox(folder_hash, envs);
|
self.load_mailbox(mailbox_hash, envs);
|
||||||
}
|
}
|
||||||
Ok(AsyncStatus::Finished) => {
|
Ok(AsyncStatus::Finished) => {
|
||||||
debug!("got finished in status for {}", folder_hash);
|
debug!("got finished in status for {}", mailbox_hash);
|
||||||
self.folder_entries.entry(folder_hash).and_modify(|entry| {
|
self.mailbox_entries
|
||||||
entry.status = MailboxStatus::Available;
|
.entry(mailbox_hash)
|
||||||
entry.worker = None;
|
.and_modify(|entry| {
|
||||||
});
|
entry.status = MailboxStatus::Available;
|
||||||
|
entry.worker = None;
|
||||||
|
});
|
||||||
self.sender
|
self.sender
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::MailboxUpdate((
|
.send(ThreadEvent::UIEvent(UIEvent::MailboxUpdate((
|
||||||
self.index,
|
self.index,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
))))
|
))))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
Ok(AsyncStatus::ProgressReport(n)) => {
|
Ok(AsyncStatus::ProgressReport(n)) => {
|
||||||
self.folder_entries.entry(folder_hash).and_modify(|entry| {
|
self.mailbox_entries
|
||||||
match entry.status {
|
.entry(mailbox_hash)
|
||||||
|
.and_modify(|entry| match entry.status {
|
||||||
MailboxStatus::Parsing(ref mut d, _) => {
|
MailboxStatus::Parsing(ref mut d, _) => {
|
||||||
*d += n;
|
*d += n;
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
//return Err(n);
|
//return Err(n);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -772,9 +770,9 @@ impl Account {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if self.folder_entries[&folder_hash].status.is_available()
|
if self.mailbox_entries[&mailbox_hash].status.is_available()
|
||||||
|| (self.folder_entries[&folder_hash].status.is_parsing()
|
|| (self.mailbox_entries[&mailbox_hash].status.is_parsing()
|
||||||
&& self.collection.mailboxes.contains_key(&folder_hash))
|
&& self.collection.mailboxes.contains_key(&mailbox_hash))
|
||||||
{
|
{
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -785,23 +783,27 @@ impl Account {
|
||||||
pub fn save_special(
|
pub fn save_special(
|
||||||
&self,
|
&self,
|
||||||
bytes: &[u8],
|
bytes: &[u8],
|
||||||
folder_type: SpecialUsageMailbox,
|
mailbox_type: SpecialUsageMailbox,
|
||||||
flags: Flag,
|
flags: Flag,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut failure = true;
|
let mut failure = true;
|
||||||
for folder in &[
|
for mailbox in &[
|
||||||
self.special_use_folder(folder_type),
|
self.special_use_mailbox(mailbox_type),
|
||||||
self.special_use_folder(SpecialUsageMailbox::Inbox),
|
self.special_use_mailbox(SpecialUsageMailbox::Inbox),
|
||||||
self.special_use_folder(SpecialUsageMailbox::Normal),
|
self.special_use_mailbox(SpecialUsageMailbox::Normal),
|
||||||
] {
|
] {
|
||||||
if folder.is_none() {
|
if mailbox.is_none() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let folder = folder.unwrap();
|
let mailbox = mailbox.unwrap();
|
||||||
if let Err(e) = self.save(bytes, folder, Some(flags)) {
|
if let Err(e) = self.save(bytes, mailbox, Some(flags)) {
|
||||||
debug!("{:?} could not save msg", e);
|
debug!("{:?} could not save msg", e);
|
||||||
melib::log(
|
melib::log(
|
||||||
format!("Could not save in '{}' folder: {}.", folder, e.to_string()),
|
format!(
|
||||||
|
"Could not save in '{}' mailbox: {}.",
|
||||||
|
mailbox,
|
||||||
|
e.to_string()
|
||||||
|
),
|
||||||
melib::ERROR,
|
melib::ERROR,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
@ -824,19 +826,19 @@ impl Account {
|
||||||
"Message was stored in {} so that you can restore it manually.",
|
"Message was stored in {} so that you can restore it manually.",
|
||||||
file.path.display()
|
file.path.display()
|
||||||
))
|
))
|
||||||
.set_summary("Could not save in any folder"));
|
.set_summary("Could not save in any mailbox"));
|
||||||
}
|
}
|
||||||
Ok(())
|
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() {
|
if self.settings.account.read_only() {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"Account {} is read-only.",
|
"Account {} is read-only.",
|
||||||
self.name.as_str()
|
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 {
|
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]
|
&self.collection.threads[&f].thread_nodes()[&h]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn folder_operation(
|
pub fn mailbox_operation(
|
||||||
&mut self,
|
&mut self,
|
||||||
op: crate::execute::actions::FolderOperation,
|
op: crate::execute::actions::MailboxOperation,
|
||||||
) -> Result<String> {
|
) -> Result<String> {
|
||||||
use crate::execute::actions::FolderOperation;
|
use crate::execute::actions::MailboxOperation;
|
||||||
if self.settings.account.read_only() {
|
if self.settings.account.read_only() {
|
||||||
return Err(MeliError::new("Account is read-only."));
|
return Err(MeliError::new("Account is read-only."));
|
||||||
}
|
}
|
||||||
match op {
|
match op {
|
||||||
FolderOperation::Create(path) => {
|
MailboxOperation::Create(path) => {
|
||||||
let (folder_hash, mut folders) = self
|
let (mailbox_hash, mut mailboxes) = self
|
||||||
.backend
|
.backend
|
||||||
.write()
|
.write()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.create_folder(path.to_string())?;
|
.create_mailbox(path.to_string())?;
|
||||||
self.sender
|
self.sender
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::MailboxCreate((
|
.send(ThreadEvent::UIEvent(UIEvent::MailboxCreate((
|
||||||
self.index,
|
self.index,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
))))
|
))))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut new = FileFolderConf::default();
|
let mut new = FileMailboxConf::default();
|
||||||
new.folder_conf.subscribe = super::ToggleFlag::InternalVal(true);
|
new.mailbox_conf.subscribe = super::ToggleFlag::InternalVal(true);
|
||||||
new.folder_conf.usage =
|
new.mailbox_conf.usage = if mailboxes[&mailbox_hash].special_usage()
|
||||||
if folders[&folder_hash].special_usage() != SpecialUsageMailbox::Normal {
|
!= SpecialUsageMailbox::Normal
|
||||||
Some(folders[&folder_hash].special_usage())
|
{
|
||||||
} else {
|
Some(mailboxes[&mailbox_hash].special_usage())
|
||||||
let tmp = SpecialUsageMailbox::detect_usage(folders[&folder_hash].name());
|
} else {
|
||||||
if tmp != Some(SpecialUsageMailbox::Normal) && tmp != None {
|
let tmp = SpecialUsageMailbox::detect_usage(mailboxes[&mailbox_hash].name());
|
||||||
folders.entry(folder_hash).and_modify(|entry| {
|
if tmp != Some(SpecialUsageMailbox::Normal) && tmp != None {
|
||||||
let _ = entry.set_special_usage(tmp.unwrap());
|
mailboxes.entry(mailbox_hash).and_modify(|entry| {
|
||||||
});
|
let _ = entry.set_special_usage(tmp.unwrap());
|
||||||
}
|
});
|
||||||
tmp
|
}
|
||||||
};
|
tmp
|
||||||
/* if new folder has parent, we need to update its children field */
|
};
|
||||||
if let Some(parent_hash) = folders[&folder_hash].parent() {
|
/* if new mailbox has parent, we need to update its children field */
|
||||||
self.folder_entries.entry(parent_hash).and_modify(|parent| {
|
if let Some(parent_hash) = mailboxes[&mailbox_hash].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.folder_entries.insert(
|
self.mailbox_entries.insert(
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
FolderEntry {
|
MailboxEntry {
|
||||||
name: folders[&folder_hash].path().to_string(),
|
name: mailboxes[&mailbox_hash].path().to_string(),
|
||||||
status: MailboxStatus::Parsing(0, 0),
|
status: MailboxStatus::Parsing(0, 0),
|
||||||
conf: new,
|
conf: new,
|
||||||
worker: Account::new_worker(
|
worker: Account::new_worker(
|
||||||
folders[&folder_hash].clone(),
|
mailboxes[&mailbox_hash].clone(),
|
||||||
&mut self.backend,
|
&mut self.backend,
|
||||||
&self.work_context,
|
&self.work_context,
|
||||||
self.notify_fn.clone(),
|
self.notify_fn.clone(),
|
||||||
),
|
),
|
||||||
ref_folder: folders.remove(&folder_hash).unwrap(),
|
ref_mailbox: mailboxes.remove(&mailbox_hash).unwrap(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.collection
|
self.collection
|
||||||
.threads
|
.threads
|
||||||
.insert(folder_hash, Threads::default());
|
.insert(mailbox_hash, Threads::default());
|
||||||
self.collection
|
self.collection
|
||||||
.mailboxes
|
.mailboxes
|
||||||
.insert(folder_hash, Default::default());
|
.insert(mailbox_hash, Default::default());
|
||||||
build_folders_order(
|
build_mailboxes_order(
|
||||||
&mut self.tree,
|
&mut self.tree,
|
||||||
&self.folder_entries,
|
&self.mailbox_entries,
|
||||||
&mut self.folders_order,
|
&mut self.mailboxes_order,
|
||||||
);
|
);
|
||||||
Ok(format!("`{}` successfully created.", &path))
|
Ok(format!("`{}` successfully created.", &path))
|
||||||
}
|
}
|
||||||
FolderOperation::Delete(path) => {
|
MailboxOperation::Delete(path) => {
|
||||||
if self.folder_entries.len() == 1 {
|
if self.mailbox_entries.len() == 1 {
|
||||||
return Err(MeliError::new("Cannot delete only mailbox."));
|
return Err(MeliError::new("Cannot delete only mailbox."));
|
||||||
}
|
}
|
||||||
let folder_hash = if let Some((folder_hash, _)) = self
|
let mailbox_hash = if let Some((mailbox_hash, _)) = self
|
||||||
.folder_entries
|
.mailbox_entries
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(_, f)| f.ref_folder.path() == path)
|
.find(|(_, f)| f.ref_mailbox.path() == path)
|
||||||
{
|
{
|
||||||
*folder_hash
|
*mailbox_hash
|
||||||
} else {
|
} else {
|
||||||
return Err(MeliError::new("Mailbox with that path not found."));
|
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
|
self.sender
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::MailboxDelete((
|
.send(ThreadEvent::UIEvent(UIEvent::MailboxDelete((
|
||||||
self.index,
|
self.index,
|
||||||
folder_hash,
|
mailbox_hash,
|
||||||
))))
|
))))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
if let Some(pos) = self.folders_order.iter().position(|&h| h == folder_hash) {
|
if let Some(pos) = self.mailboxes_order.iter().position(|&h| h == mailbox_hash) {
|
||||||
self.folders_order.remove(pos);
|
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);
|
self.tree.remove(pos);
|
||||||
}
|
}
|
||||||
if self.sent_folder == Some(folder_hash) {
|
if self.sent_mailbox == Some(mailbox_hash) {
|
||||||
self.sent_folder = None;
|
self.sent_mailbox = None;
|
||||||
}
|
}
|
||||||
self.collection.threads.remove(&folder_hash);
|
self.collection.threads.remove(&mailbox_hash);
|
||||||
/* if deleted folder had parent, we need to update its children field */
|
/* if deleted mailbox had parent, we need to update its children field */
|
||||||
if let Some(parent_hash) = self
|
if let Some(parent_hash) = self
|
||||||
.folder_entries
|
.mailbox_entries
|
||||||
.remove(&folder_hash)
|
.remove(&mailbox_hash)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.ref_folder
|
.ref_mailbox
|
||||||
.parent()
|
.parent()
|
||||||
{
|
{
|
||||||
self.folder_entries.entry(parent_hash).and_modify(|parent| {
|
self.mailbox_entries
|
||||||
parent.ref_folder = folders.remove(&parent_hash).unwrap();
|
.entry(parent_hash)
|
||||||
});
|
.and_modify(|parent| {
|
||||||
|
parent.ref_mailbox = mailboxes.remove(&parent_hash).unwrap();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
self.collection.mailboxes.remove(&folder_hash);
|
self.collection.mailboxes.remove(&mailbox_hash);
|
||||||
build_folders_order(
|
build_mailboxes_order(
|
||||||
&mut self.tree,
|
&mut self.tree,
|
||||||
&self.folder_entries,
|
&self.mailbox_entries,
|
||||||
&mut self.folders_order,
|
&mut self.mailboxes_order,
|
||||||
);
|
);
|
||||||
// FIXME Kill worker as well
|
// FIXME Kill worker as well
|
||||||
|
|
||||||
|
@ -979,18 +986,18 @@ impl Account {
|
||||||
|
|
||||||
Ok(format!("'`{}` has been deleted.", &path))
|
Ok(format!("'`{}` has been deleted.", &path))
|
||||||
}
|
}
|
||||||
FolderOperation::Subscribe(_) => Err(MeliError::new("Not implemented.")),
|
MailboxOperation::Subscribe(_) => Err(MeliError::new("Not implemented.")),
|
||||||
FolderOperation::Unsubscribe(_) => Err(MeliError::new("Not implemented.")),
|
MailboxOperation::Unsubscribe(_) => Err(MeliError::new("Not implemented.")),
|
||||||
FolderOperation::Rename(_, _) => Err(MeliError::new("Not implemented.")),
|
MailboxOperation::Rename(_, _) => Err(MeliError::new("Not implemented.")),
|
||||||
FolderOperation::SetPermissions(_) => 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
|
let ret = self
|
||||||
.folder_entries
|
.mailbox_entries
|
||||||
.iter()
|
.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() {
|
if let Some(ret) = ret.as_ref() {
|
||||||
Some(ret.1.name())
|
Some(ret.1.name())
|
||||||
} else {
|
} else {
|
||||||
|
@ -1017,10 +1024,10 @@ impl Account {
|
||||||
&self,
|
&self,
|
||||||
search_term: &str,
|
search_term: &str,
|
||||||
sort: (SortField, SortOrder),
|
sort: (SortField, SortOrder),
|
||||||
folder_hash: FolderHash,
|
mailbox_hash: MailboxHash,
|
||||||
) -> Result<SmallVec<[EnvelopeHash; 512]>> {
|
) -> Result<SmallVec<[EnvelopeHash; 512]>> {
|
||||||
if self.settings.account().format() == "imap" {
|
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")]
|
#[cfg(feature = "notmuch")]
|
||||||
|
@ -1049,7 +1056,7 @@ impl Account {
|
||||||
let mut ret = SmallVec::new();
|
let mut ret = SmallVec::new();
|
||||||
let envelopes = self.collection.envelopes.read().unwrap();
|
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];
|
let envelope = &envelopes[&env_hash];
|
||||||
if envelope.subject().contains(&search_term) {
|
if envelope.subject().contains(&search_term) {
|
||||||
ret.push(env_hash);
|
ret.push(env_hash);
|
||||||
|
@ -1072,97 +1079,97 @@ impl Account {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<&FolderHash> for Account {
|
impl Index<&MailboxHash> for Account {
|
||||||
type Output = FolderEntry;
|
type Output = MailboxEntry;
|
||||||
fn index(&self, index: &FolderHash) -> &FolderEntry {
|
fn index(&self, index: &MailboxHash) -> &MailboxEntry {
|
||||||
&self.folder_entries[index]
|
&self.mailbox_entries[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexMut<&FolderHash> for Account {
|
impl IndexMut<&MailboxHash> for Account {
|
||||||
fn index_mut(&mut self, index: &FolderHash) -> &mut FolderEntry {
|
fn index_mut(&mut self, index: &MailboxHash) -> &mut MailboxEntry {
|
||||||
self.folder_entries.get_mut(index).unwrap()
|
self.mailbox_entries.get_mut(index).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_folders_order(
|
fn build_mailboxes_order(
|
||||||
tree: &mut Vec<FolderNode>,
|
tree: &mut Vec<MailboxNode>,
|
||||||
folder_entries: &FnvHashMap<FolderHash, FolderEntry>,
|
mailbox_entries: &FnvHashMap<MailboxHash, MailboxEntry>,
|
||||||
folders_order: &mut Vec<FolderHash>,
|
mailboxes_order: &mut Vec<MailboxHash>,
|
||||||
) {
|
) {
|
||||||
tree.clear();
|
tree.clear();
|
||||||
folders_order.clear();
|
mailboxes_order.clear();
|
||||||
for (h, f) in folder_entries.iter() {
|
for (h, f) in mailbox_entries.iter() {
|
||||||
if f.ref_folder.parent().is_none() {
|
if f.ref_mailbox.parent().is_none() {
|
||||||
fn rec(
|
fn rec(
|
||||||
h: FolderHash,
|
h: MailboxHash,
|
||||||
folder_entries: &FnvHashMap<FolderHash, FolderEntry>,
|
mailbox_entries: &FnvHashMap<MailboxHash, MailboxEntry>,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
) -> FolderNode {
|
) -> MailboxNode {
|
||||||
let mut node = FolderNode {
|
let mut node = MailboxNode {
|
||||||
hash: h,
|
hash: h,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
depth,
|
depth,
|
||||||
};
|
};
|
||||||
for &c in folder_entries[&h].ref_folder.children() {
|
for &c in mailbox_entries[&h].ref_mailbox.children() {
|
||||||
if folder_entries.contains_key(&c) {
|
if mailbox_entries.contains_key(&c) {
|
||||||
node.children.push(rec(c, folder_entries, depth + 1));
|
node.children.push(rec(c, mailbox_entries, depth + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
node
|
node
|
||||||
};
|
};
|
||||||
|
|
||||||
tree.push(rec(*h, &folder_entries, 0));
|
tree.push(rec(*h, &mailbox_entries, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tree.sort_unstable_by(|a, b| {
|
tree.sort_unstable_by(|a, b| {
|
||||||
if folder_entries[&b.hash]
|
if mailbox_entries[&b.hash]
|
||||||
.ref_folder
|
.ref_mailbox
|
||||||
.path()
|
.path()
|
||||||
.eq_ignore_ascii_case("INBOX")
|
.eq_ignore_ascii_case("INBOX")
|
||||||
{
|
{
|
||||||
std::cmp::Ordering::Greater
|
std::cmp::Ordering::Greater
|
||||||
} else if folder_entries[&a.hash]
|
} else if mailbox_entries[&a.hash]
|
||||||
.ref_folder
|
.ref_mailbox
|
||||||
.path()
|
.path()
|
||||||
.eq_ignore_ascii_case("INBOX")
|
.eq_ignore_ascii_case("INBOX")
|
||||||
{
|
{
|
||||||
std::cmp::Ordering::Less
|
std::cmp::Ordering::Less
|
||||||
} else {
|
} else {
|
||||||
folder_entries[&a.hash]
|
mailbox_entries[&a.hash]
|
||||||
.ref_folder
|
.ref_mailbox
|
||||||
.path()
|
.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() {
|
for n in tree.iter_mut() {
|
||||||
folders_order.push(n.hash);
|
mailboxes_order.push(n.hash);
|
||||||
n.children.sort_unstable_by(|a, b| {
|
n.children.sort_unstable_by(|a, b| {
|
||||||
if folder_entries[&b.hash]
|
if mailbox_entries[&b.hash]
|
||||||
.ref_folder
|
.ref_mailbox
|
||||||
.path()
|
.path()
|
||||||
.eq_ignore_ascii_case("INBOX")
|
.eq_ignore_ascii_case("INBOX")
|
||||||
{
|
{
|
||||||
std::cmp::Ordering::Greater
|
std::cmp::Ordering::Greater
|
||||||
} else if folder_entries[&a.hash]
|
} else if mailbox_entries[&a.hash]
|
||||||
.ref_folder
|
.ref_mailbox
|
||||||
.path()
|
.path()
|
||||||
.eq_ignore_ascii_case("INBOX")
|
.eq_ignore_ascii_case("INBOX")
|
||||||
{
|
{
|
||||||
std::cmp::Ordering::Less
|
std::cmp::Ordering::Less
|
||||||
} else {
|
} else {
|
||||||
folder_entries[&a.hash]
|
mailbox_entries[&a.hash]
|
||||||
.ref_folder
|
.ref_mailbox
|
||||||
.path()
|
.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));
|
stack.extend(n.children.iter().rev().map(Some));
|
||||||
while let Some(Some(next)) = stack.pop() {
|
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));
|
stack.extend(next.children.iter().rev().map(Some));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,13 +109,13 @@ shortcut_key_values! { "listing",
|
||||||
scroll_down |> "Scroll down list." |> Key::Down,
|
scroll_down |> "Scroll down list." |> Key::Down,
|
||||||
new_mail |> "Start new mail draft in new tab." |> Key::Char('m'),
|
new_mail |> "Start new mail draft in new tab." |> Key::Char('m'),
|
||||||
next_account |> "Go to next account." |> Key::Char('h'),
|
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,
|
next_page |> "Go to next page." |> Key::PageDown,
|
||||||
prev_account |> "Go to previous account." |> Key::Char('l'),
|
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,
|
prev_page |> "Go to previous page." |> Key::PageUp,
|
||||||
search |> "Search within list of e-mails." |> Key::Char('/'),
|
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'),
|
set_seen |> "Set thread as seen." |> Key::Char('n'),
|
||||||
toggle_menu_visibility |> "Toggle visibility of side menu in mail list." |> Key::Char('`')
|
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 nom::{digit, not_line_ending, IResult};
|
||||||
use std;
|
use std;
|
||||||
pub mod actions;
|
pub mod actions;
|
||||||
use actions::FolderOperation;
|
use actions::MailboxOperation;
|
||||||
pub mod history;
|
pub mod history;
|
||||||
pub use crate::actions::AccountAction::{self, *};
|
pub use crate::actions::AccountAction::{self, *};
|
||||||
pub use crate::actions::Action::{self, *};
|
pub use crate::actions::Action::{self, *};
|
||||||
|
@ -247,74 +247,74 @@ define_commands!([
|
||||||
);
|
);
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{ tags: ["create-folder "],
|
{ tags: ["create-mailbox "],
|
||||||
desc: "create-folder ACCOUNT FOLDER_PATH",
|
desc: "create-mailbox ACCOUNT MAILBOX_PATH",
|
||||||
parser:(
|
parser:(
|
||||||
named!( create_folder<Action>,
|
named!( create_mailbox<Action>,
|
||||||
do_parse!(
|
do_parse!(
|
||||||
ws!(tag!("create-folder"))
|
ws!(tag!("create-mailbox"))
|
||||||
>> account: quoted_argument
|
>> account: quoted_argument
|
||||||
>> is_a!(" ")
|
>> is_a!(" ")
|
||||||
>> path: quoted_argument
|
>> path: quoted_argument
|
||||||
>> (Folder(account.to_string(), FolderOperation::Create(path.to_string())))
|
>> (Mailbox(account.to_string(), MailboxOperation::Create(path.to_string())))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{ tags: ["subscribe-folder "],
|
{ tags: ["subscribe-mailbox "],
|
||||||
desc: "subscribe-folder ACCOUNT FOLDER_PATH",
|
desc: "subscribe-mailbox ACCOUNT MAILBOX_PATH",
|
||||||
parser:(
|
parser:(
|
||||||
named!( sub_folder<Action>,
|
named!( sub_mailbox<Action>,
|
||||||
do_parse!(
|
do_parse!(
|
||||||
ws!(tag!("subscribe-folder"))
|
ws!(tag!("subscribe-mailbox"))
|
||||||
>> account: quoted_argument
|
>> account: quoted_argument
|
||||||
>> is_a!(" ")
|
>> is_a!(" ")
|
||||||
>> path: quoted_argument
|
>> path: quoted_argument
|
||||||
>> (Folder(account.to_string(), FolderOperation::Subscribe(path.to_string())))
|
>> (Mailbox(account.to_string(), MailboxOperation::Subscribe(path.to_string())))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{ tags: ["unsubscribe-folder "],
|
{ tags: ["unsubscribe-mailbox "],
|
||||||
desc: "unsubscribe-folder ACCOUNT FOLDER_PATH",
|
desc: "unsubscribe-mailbox ACCOUNT MAILBOX_PATH",
|
||||||
parser:(
|
parser:(
|
||||||
named!( unsub_folder<Action>,
|
named!( unsub_mailbox<Action>,
|
||||||
do_parse!(
|
do_parse!(
|
||||||
ws!(tag!("unsubscribe-folder"))
|
ws!(tag!("unsubscribe-mailbox"))
|
||||||
>> account: quoted_argument
|
>> account: quoted_argument
|
||||||
>> is_a!(" ")
|
>> is_a!(" ")
|
||||||
>> path: quoted_argument
|
>> path: quoted_argument
|
||||||
>> (Folder(account.to_string(), FolderOperation::Unsubscribe(path.to_string())))
|
>> (Mailbox(account.to_string(), MailboxOperation::Unsubscribe(path.to_string())))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{ tags: ["rename-folder "],
|
{ tags: ["rename-mailbox "],
|
||||||
desc: "rename-folder ACCOUNT FOLDER_PATH_SRC FOLDER_PATH_DEST",
|
desc: "rename-mailbox ACCOUNT MAILBOX_PATH_SRC MAILBOX_PATH_DEST",
|
||||||
parser:(
|
parser:(
|
||||||
named!( rename_folder<Action>,
|
named!( rename_mailbox<Action>,
|
||||||
do_parse!(
|
do_parse!(
|
||||||
ws!(tag!("rename-folder"))
|
ws!(tag!("rename-mailbox"))
|
||||||
>> account: quoted_argument
|
>> account: quoted_argument
|
||||||
>> is_a!(" ")
|
>> is_a!(" ")
|
||||||
>> src: quoted_argument
|
>> src: quoted_argument
|
||||||
>> is_a!(" ")
|
>> is_a!(" ")
|
||||||
>> dest: quoted_argument
|
>> 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 "],
|
{ tags: ["delete-mailbox "],
|
||||||
desc: "delete-folder ACCOUNT FOLDER_PATH",
|
desc: "delete-mailbox ACCOUNT MAILBOX_PATH",
|
||||||
parser:(
|
parser:(
|
||||||
named!( delete_folder<Action>,
|
named!( delete_mailbox<Action>,
|
||||||
do_parse!(
|
do_parse!(
|
||||||
ws!(tag!("delete-folder"))
|
ws!(tag!("delete-mailbox"))
|
||||||
>> account: quoted_argument
|
>> account: quoted_argument
|
||||||
>> is_a!(" ")
|
>> is_a!(" ")
|
||||||
>> path: quoted_argument
|
>> 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!(view<Action>, alt_complete!(pipe | save_attachment));
|
||||||
|
|
||||||
named!(pub parse_command<Action>,
|
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 crate::components::Component;
|
||||||
use melib::backends::FolderHash;
|
use melib::backends::MailboxHash;
|
||||||
pub use melib::thread::{SortField, SortOrder};
|
pub use melib::thread::{SortField, SortOrder};
|
||||||
use melib::{Draft, EnvelopeHash};
|
use melib::{Draft, EnvelopeHash};
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ pub enum ListingAction {
|
||||||
pub enum TabAction {
|
pub enum TabAction {
|
||||||
New(Option<Box<dyn Component>>),
|
New(Option<Box<dyn Component>>),
|
||||||
NewDraft(usize, Option<Draft>),
|
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,
|
Close,
|
||||||
Edit(usize, EnvelopeHash), // account_position, envelope hash
|
Edit(usize, EnvelopeHash), // account_position, envelope hash
|
||||||
Kill(Uuid),
|
Kill(Uuid),
|
||||||
|
@ -87,14 +87,14 @@ pub enum AccountAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum FolderOperation {
|
pub enum MailboxOperation {
|
||||||
Create(NewFolderPath),
|
Create(NewMailboxPath),
|
||||||
Delete(FolderPath),
|
Delete(MailboxPath),
|
||||||
Subscribe(FolderPath),
|
Subscribe(MailboxPath),
|
||||||
Unsubscribe(FolderPath),
|
Unsubscribe(MailboxPath),
|
||||||
Rename(FolderPath, NewFolderPath),
|
Rename(MailboxPath, NewMailboxPath),
|
||||||
// Placeholder
|
// Placeholder
|
||||||
SetPermissions(FolderPath),
|
SetPermissions(MailboxPath),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -110,7 +110,7 @@ pub enum Action {
|
||||||
SetEnv(String, String),
|
SetEnv(String, String),
|
||||||
PrintEnv(String),
|
PrintEnv(String),
|
||||||
Compose(ComposeAction),
|
Compose(ComposeAction),
|
||||||
Folder(AccountName, FolderOperation),
|
Mailbox(AccountName, MailboxOperation),
|
||||||
AccountAction(AccountName, AccountAction),
|
AccountAction(AccountName, AccountAction),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,12 +128,12 @@ impl Action {
|
||||||
Action::SetEnv(_, _) => false,
|
Action::SetEnv(_, _) => false,
|
||||||
Action::PrintEnv(_) => false,
|
Action::PrintEnv(_) => false,
|
||||||
Action::Compose(_) => false,
|
Action::Compose(_) => false,
|
||||||
Action::Folder(_, _) => true,
|
Action::Mailbox(_, _) => true,
|
||||||
Action::AccountAction(_, _) => false,
|
Action::AccountAction(_, _) => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccountName = String;
|
type AccountName = String;
|
||||||
type FolderPath = String;
|
type MailboxPath = String;
|
||||||
type NewFolderPath = String;
|
type NewMailboxPath = String;
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
use super::*;
|
use super::*;
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use melib::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
use melib::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
|
||||||
use melib::backends::FolderHash;
|
use melib::backends::MailboxHash;
|
||||||
use melib::backends::{Backend, BackendOp, Backends, Folder, MailBackend, RefreshEventConsumer};
|
use melib::backends::{Backend, BackendOp, Backends, MailBackend, Mailbox, RefreshEventConsumer};
|
||||||
use melib::conf::AccountSettings;
|
use melib::conf::AccountSettings;
|
||||||
use melib::email::{Envelope, EnvelopeHash, Flag};
|
use melib::email::{Envelope, EnvelopeHash, Flag};
|
||||||
use melib::error::{MeliError, Result};
|
use melib::error::{MeliError, Result};
|
||||||
|
@ -86,9 +86,9 @@ impl MailBackend for PluginBackend {
|
||||||
|
|
||||||
fn connect(&mut self) {}
|
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 mut w = AsyncBuilder::new();
|
||||||
let _folder_hash = folder.hash();
|
let _mailbox_hash = mailbox.hash();
|
||||||
let channel = self.channel.clone();
|
let channel = self.channel.clone();
|
||||||
let handle = {
|
let handle = {
|
||||||
let tx = w.tx();
|
let tx = w.tx();
|
||||||
|
@ -180,7 +180,7 @@ impl MailBackend for PluginBackend {
|
||||||
|
|
||||||
fn refresh(
|
fn refresh(
|
||||||
&mut self,
|
&mut self,
|
||||||
_folder_hash: FolderHash,
|
_mailbox_hash: MailboxHash,
|
||||||
_sender: RefreshEventConsumer,
|
_sender: RefreshEventConsumer,
|
||||||
) -> Result<Async<()>> {
|
) -> Result<Async<()>> {
|
||||||
Err(MeliError::new("Unimplemented."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
|
@ -193,9 +193,9 @@ impl MailBackend for PluginBackend {
|
||||||
Err(MeliError::new("Unimplemented."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn folders(&self) -> Result<FnvHashMap<FolderHash, Folder>> {
|
fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
|
||||||
let mut ret: FnvHashMap<FolderHash, Folder> = Default::default();
|
let mut ret: FnvHashMap<MailboxHash, Mailbox> = Default::default();
|
||||||
ret.insert(0, Folder::default());
|
ret.insert(0, Mailbox::default());
|
||||||
Ok(ret)
|
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."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
}
|
}
|
||||||
fn create_folder(
|
fn create_mailbox(
|
||||||
&mut self,
|
&mut self,
|
||||||
_name: String,
|
_name: String,
|
||||||
) -> Result<(FolderHash, FnvHashMap<FolderHash, Folder>)> {
|
) -> Result<(MailboxHash, FnvHashMap<MailboxHash, Mailbox>)> {
|
||||||
Err(MeliError::new("Unimplemented."))
|
Err(MeliError::new("Unimplemented."))
|
||||||
}
|
}
|
||||||
fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
|
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 super::*;
|
||||||
use crate::plugins::PluginManager;
|
use crate::plugins::PluginManager;
|
||||||
use melib::backends::{FolderHash, NotifyFn};
|
use melib::backends::{MailboxHash, NotifyFn};
|
||||||
|
|
||||||
use crossbeam::channel::{unbounded, Receiver, Sender};
|
use crossbeam::channel::{unbounded, Receiver, Sender};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
|
@ -85,7 +85,7 @@ impl InputHandler {
|
||||||
/// A context container for loaded settings, accounts, UI changes, etc.
|
/// A context container for loaded settings, accounts, UI changes, etc.
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
pub accounts: Vec<Account>,
|
pub accounts: Vec<Account>,
|
||||||
pub mailbox_hashes: FnvHashMap<FolderHash, usize>,
|
pub mailbox_hashes: FnvHashMap<MailboxHash, usize>,
|
||||||
pub settings: Settings,
|
pub settings: Settings,
|
||||||
|
|
||||||
pub runtime_settings: Settings,
|
pub runtime_settings: Settings,
|
||||||
|
@ -126,12 +126,12 @@ impl Context {
|
||||||
pub fn account_status(
|
pub fn account_status(
|
||||||
&mut self,
|
&mut self,
|
||||||
idx_a: usize,
|
idx_a: usize,
|
||||||
folder_hash: FolderHash,
|
mailbox_hash: MailboxHash,
|
||||||
) -> result::Result<(), usize> {
|
) -> result::Result<(), usize> {
|
||||||
match self.accounts[idx_a].status(folder_hash) {
|
match self.accounts[idx_a].status(mailbox_hash) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
self.replies
|
self.replies
|
||||||
.push_back(UIEvent::MailboxUpdate((idx_a, folder_hash)));
|
.push_back(UIEvent::MailboxUpdate((idx_a, mailbox_hash)));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(n) => Err(n),
|
Err(n) => Err(n),
|
||||||
|
@ -149,13 +149,13 @@ impl Context {
|
||||||
let ret = accounts[account_pos].is_online();
|
let ret = accounts[account_pos].is_online();
|
||||||
if ret.is_ok() {
|
if ret.is_ok() {
|
||||||
if !was_online {
|
if !was_online {
|
||||||
for folder_node in accounts[account_pos].list_folders() {
|
for mailbox_node in accounts[account_pos].list_mailboxes() {
|
||||||
debug!(
|
debug!(
|
||||||
"hash & folder: {:?} {}",
|
"hash & mailbox: {:?} {}",
|
||||||
folder_node.hash,
|
mailbox_node.hash,
|
||||||
accounts[account_pos][&folder_node.hash].name()
|
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
|
/* Account::watch() needs
|
||||||
* - work_controller to pass `work_context` to the watcher threads and then add them
|
* - work_controller to pass `work_context` to the watcher threads and then add them
|
||||||
|
@ -267,7 +267,7 @@ impl State {
|
||||||
&backends,
|
&backends,
|
||||||
work_controller.get_context(),
|
work_controller.get_context(),
|
||||||
sender.clone(),
|
sender.clone(),
|
||||||
NotifyFn::new(Box::new(move |f: FolderHash| {
|
NotifyFn::new(Box::new(move |f: MailboxHash| {
|
||||||
sender
|
sender
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::WorkerProgress(f)))
|
.send(ThreadEvent::UIEvent(UIEvent::WorkerProgress(f)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -347,7 +347,7 @@ impl State {
|
||||||
for i in 0..s.context.accounts.len() {
|
for i in 0..s.context.accounts.len() {
|
||||||
if s.context.is_online(i).is_ok() && s.context.accounts[i].is_empty() {
|
if s.context.is_online(i).is_ok() && s.context.accounts[i].is_empty() {
|
||||||
return Err(MeliError::new(format!(
|
return Err(MeliError::new(format!(
|
||||||
"Account {} has no folders configured.",
|
"Account {} has no mailboxes configured.",
|
||||||
s.context.accounts[i].name()
|
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
|
* 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.
|
* 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()),
|
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
|
if let Some(account) = self
|
||||||
.context
|
.context
|
||||||
.accounts
|
.accounts
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.find(|a| a.name() == account_name)
|
.find(|a| a.name() == account_name)
|
||||||
{
|
{
|
||||||
match account.folder_operation(op) {
|
match account.mailbox_operation(op) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.context.replies.push_back(UIEvent::StatusEvent(
|
self.context.replies.push_back(UIEvent::StatusEvent(
|
||||||
StatusEvent::DisplayMessage(err.to_string()),
|
StatusEvent::DisplayMessage(err.to_string()),
|
||||||
|
@ -911,9 +911,9 @@ impl State {
|
||||||
self.child = Some(child);
|
self.child = Some(child);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
UIEvent::WorkerProgress(folder_hash) => {
|
UIEvent::WorkerProgress(mailbox_hash) => {
|
||||||
if let Some(&account_idx) = self.context.mailbox_hashes.get(&folder_hash) {
|
if let Some(&account_idx) = self.context.mailbox_hashes.get(&mailbox_hash) {
|
||||||
let _ = self.context.accounts[account_idx].status(folder_hash);
|
let _ = self.context.accounts[account_idx].status(mailbox_hash);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
14
src/types.rs
14
src/types.rs
|
@ -38,7 +38,7 @@ pub use self::helpers::*;
|
||||||
use super::execute::Action;
|
use super::execute::Action;
|
||||||
use super::terminal::*;
|
use super::terminal::*;
|
||||||
|
|
||||||
use melib::backends::FolderHash;
|
use melib::backends::MailboxHash;
|
||||||
use melib::{EnvelopeHash, RefreshEvent};
|
use melib::{EnvelopeHash, RefreshEvent};
|
||||||
use nix::unistd::Pid;
|
use nix::unistd::Pid;
|
||||||
use std;
|
use std;
|
||||||
|
@ -63,7 +63,7 @@ pub enum ThreadEvent {
|
||||||
Input(Key),
|
Input(Key),
|
||||||
/// User input and input as raw bytes.
|
/// User input and input as raw bytes.
|
||||||
InputRaw((Key, Vec<u8>)),
|
InputRaw((Key, Vec<u8>)),
|
||||||
/// A watched folder has been refreshed.
|
/// A watched Mailbox has been refreshed.
|
||||||
RefreshMailbox(Box<RefreshEvent>),
|
RefreshMailbox(Box<RefreshEvent>),
|
||||||
UIEvent(UIEvent),
|
UIEvent(UIEvent),
|
||||||
/// A thread has updated some of its information
|
/// A thread has updated some of its information
|
||||||
|
@ -110,13 +110,13 @@ pub enum UIEvent {
|
||||||
Notification(Option<String>, String, Option<NotificationType>),
|
Notification(Option<String>, String, Option<NotificationType>),
|
||||||
Action(Action),
|
Action(Action),
|
||||||
StatusEvent(StatusEvent),
|
StatusEvent(StatusEvent),
|
||||||
MailboxUpdate((usize, FolderHash)), // (account_idx, mailbox_idx)
|
MailboxUpdate((usize, MailboxHash)), // (account_idx, mailbox_idx)
|
||||||
MailboxDelete((usize, FolderHash)),
|
MailboxDelete((usize, MailboxHash)),
|
||||||
MailboxCreate((usize, FolderHash)),
|
MailboxCreate((usize, MailboxHash)),
|
||||||
AccountStatusChange(usize),
|
AccountStatusChange(usize),
|
||||||
ComponentKill(Uuid),
|
ComponentKill(Uuid),
|
||||||
WorkerProgress(FolderHash),
|
WorkerProgress(MailboxHash),
|
||||||
StartupCheck(FolderHash),
|
StartupCheck(MailboxHash),
|
||||||
RefreshEvent(Box<RefreshEvent>),
|
RefreshEvent(Box<RefreshEvent>),
|
||||||
EnvelopeUpdate(EnvelopeHash),
|
EnvelopeUpdate(EnvelopeHash),
|
||||||
EnvelopeRename(EnvelopeHash, EnvelopeHash), // old_hash, new_hash
|
EnvelopeRename(EnvelopeHash, EnvelopeHash), // old_hash, new_hash
|
||||||
|
|
Loading…
Reference in New Issue