Compare commits
4 commits
master
...
imap-lemon
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b2ff359eb7 | ||
![]() |
9fcf88b494 | ||
![]() |
808bdf75a1 | ||
![]() |
ed16e29de1 |
21 changed files with 213 additions and 184 deletions
|
@ -257,6 +257,27 @@ impl Backends {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug, Serialize, Deserialize, Clone, PartialOrd, Ord, Default, Copy, Hash, PartialEq, Eq,
|
||||
)]
|
||||
pub struct AccountHash(pub u64);
|
||||
|
||||
#[derive(
|
||||
Debug, Serialize, Deserialize, Clone, PartialOrd, Ord, Default, Copy, Hash, PartialEq, Eq,
|
||||
)]
|
||||
pub struct MailboxHash(pub u64);
|
||||
|
||||
impl std::fmt::Display for AccountHash {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "AccountHash({})", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MailboxHash {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "MailboxHash({})", self.0)
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum BackendEvent {
|
||||
Notice {
|
||||
|
@ -589,8 +610,6 @@ pub trait BackendMailbox: Debug {
|
|||
fn count(&self) -> Result<(usize, usize)>;
|
||||
}
|
||||
|
||||
pub type AccountHash = u64;
|
||||
pub type MailboxHash = u64;
|
||||
pub type Mailbox = Box<dyn BackendMailbox + Send + Sync>;
|
||||
|
||||
impl Clone for Mailbox {
|
||||
|
|
|
@ -66,6 +66,7 @@ pub type MessageSequenceNumber = ImapNum;
|
|||
|
||||
pub static SUPPORTED_CAPABILITIES: &[&str] = &[
|
||||
"AUTH=OAUTH2",
|
||||
"CHILDREN",
|
||||
#[cfg(feature = "deflate_compression")]
|
||||
"COMPRESS=DEFLATE",
|
||||
"CONDSTORE",
|
||||
|
@ -505,7 +506,7 @@ impl MailBackend for ImapType {
|
|||
let account_hash = uid_store.account_hash;
|
||||
main_conn_lck.add_refresh_event(RefreshEvent {
|
||||
account_hash,
|
||||
mailbox_hash: 0,
|
||||
mailbox_hash: MailboxHash(0),
|
||||
kind: RefreshEventKind::Failure(err.clone()),
|
||||
});
|
||||
return Err(err);
|
||||
|
@ -938,7 +939,7 @@ impl MailBackend for ImapType {
|
|||
}
|
||||
let ret: Result<()> = ImapResponse::try_from(response.as_slice())?.into();
|
||||
ret?;
|
||||
let new_hash = get_path_hash!(path.as_str());
|
||||
let new_hash = MailboxHash(get_path_hash!(path.as_str()));
|
||||
uid_store.mailboxes.lock().await.clear();
|
||||
Ok((new_hash, new_mailbox_fut?.await.map_err(|err| MeliError::new(format!("Mailbox create was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", String::from_utf8_lossy(&response), err)))?))
|
||||
}))
|
||||
|
@ -1075,7 +1076,7 @@ impl MailBackend for ImapType {
|
|||
.read_response(&mut response, RequiredResponses::empty())
|
||||
.await?;
|
||||
}
|
||||
let new_hash = get_path_hash!(new_path.as_str());
|
||||
let new_hash = MailboxHash(get_path_hash!(new_path.as_str()));
|
||||
let ret: Result<()> = ImapResponse::try_from(response.as_slice())?.into();
|
||||
ret?;
|
||||
uid_store.mailboxes.lock().await.clear();
|
||||
|
@ -1322,7 +1323,7 @@ impl ImapType {
|
|||
let account_hash = {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
hasher.write(s.name.as_bytes());
|
||||
hasher.finish()
|
||||
AccountHash(hasher.finish())
|
||||
};
|
||||
let account_name = Arc::new(s.name().to_string());
|
||||
let uid_store: Arc<UIDStore> = Arc::new(UIDStore {
|
||||
|
@ -1475,7 +1476,7 @@ impl ImapType {
|
|||
debug!("parse error for {:?}", l);
|
||||
}
|
||||
}
|
||||
mailboxes.retain(|_, v| v.hash != 0);
|
||||
mailboxes.retain(|_, v| v.hash.0 != 0);
|
||||
conn.send_command(b"LSUB \"\" \"*\"").await?;
|
||||
conn.read_response(&mut res, RequiredResponses::LSUB_REQUIRED)
|
||||
.await?;
|
||||
|
|
|
@ -177,7 +177,7 @@ mod sqlite3_m {
|
|||
.prepare("SELECT MAX(uid) FROM envelopes WHERE mailbox_hash = ?1;")?;
|
||||
|
||||
let mut ret: Vec<UID> = stmt
|
||||
.query_map(sqlite3::params![mailbox_hash as i64], |row| {
|
||||
.query_map(sqlite3::params![mailbox_hash.0 as i64], |row| {
|
||||
Ok(row.get(0).map(|i: Sqlite3UID| i as UID)?)
|
||||
})?
|
||||
.collect::<std::result::Result<_, _>>()?;
|
||||
|
@ -199,7 +199,7 @@ mod sqlite3_m {
|
|||
"SELECT uidvalidity, flags, highestmodseq FROM mailbox WHERE mailbox_hash = ?1;",
|
||||
)?;
|
||||
|
||||
let mut ret = stmt.query_map(sqlite3::params![mailbox_hash as i64], |row| {
|
||||
let mut ret = stmt.query_map(sqlite3::params![mailbox_hash.0 as i64], |row| {
|
||||
Ok((
|
||||
row.get(0).map(|u: Sqlite3UID| u as UID)?,
|
||||
row.get(1)?,
|
||||
|
@ -265,7 +265,7 @@ mod sqlite3_m {
|
|||
self.connection
|
||||
.execute(
|
||||
"DELETE FROM mailbox WHERE mailbox_hash = ?1",
|
||||
sqlite3::params![mailbox_hash as i64],
|
||||
sqlite3::params![mailbox_hash.0 as i64],
|
||||
)
|
||||
.chain_err_summary(|| {
|
||||
format!(
|
||||
|
@ -277,7 +277,7 @@ mod sqlite3_m {
|
|||
if let Some(Ok(highestmodseq)) = select_response.highestmodseq {
|
||||
self.connection.execute(
|
||||
"INSERT OR IGNORE INTO mailbox (uidvalidity, flags, highestmodseq, mailbox_hash) VALUES (?1, ?2, ?3, ?4)",
|
||||
sqlite3::params![select_response.uidvalidity as Sqlite3UID, select_response.flags.1.iter().map(|s| s.as_str()).collect::<Vec<&str>>().join("\0").as_bytes(), highestmodseq, mailbox_hash as i64],
|
||||
sqlite3::params![select_response.uidvalidity as Sqlite3UID, select_response.flags.1.iter().map(|s| s.as_str()).collect::<Vec<&str>>().join("\0").as_bytes(), highestmodseq, mailbox_hash.0 as i64],
|
||||
)
|
||||
.chain_err_summary(|| {
|
||||
format!(
|
||||
|
@ -292,7 +292,7 @@ mod sqlite3_m {
|
|||
sqlite3::params![
|
||||
select_response.uidvalidity as Sqlite3UID,
|
||||
select_response.flags.1.iter().map(|s| s.as_str()).collect::<Vec<&str>>().join("\0").as_bytes(),
|
||||
mailbox_hash as i64
|
||||
mailbox_hash.0 as i64
|
||||
],
|
||||
)
|
||||
.chain_err_summary(|| {
|
||||
|
@ -328,7 +328,7 @@ mod sqlite3_m {
|
|||
.join("\0")
|
||||
.as_bytes(),
|
||||
highestmodseq,
|
||||
mailbox_hash as i64
|
||||
mailbox_hash.0 as i64
|
||||
],
|
||||
)
|
||||
.chain_err_summary(|| {
|
||||
|
@ -350,7 +350,7 @@ mod sqlite3_m {
|
|||
.collect::<Vec<&str>>()
|
||||
.join("\0")
|
||||
.as_bytes(),
|
||||
mailbox_hash as i64
|
||||
mailbox_hash.0 as i64
|
||||
],
|
||||
)
|
||||
.chain_err_summary(|| {
|
||||
|
@ -374,7 +374,7 @@ mod sqlite3_m {
|
|||
)?;
|
||||
|
||||
let ret: Vec<(UID, Envelope, Option<ModSequence>)> = stmt
|
||||
.query_map(sqlite3::params![mailbox_hash as i64], |row| {
|
||||
.query_map(sqlite3::params![mailbox_hash.0 as i64], |row| {
|
||||
Ok((
|
||||
row.get(0).map(|i: Sqlite3UID| i as UID)?,
|
||||
row.get(1)?,
|
||||
|
@ -452,7 +452,7 @@ mod sqlite3_m {
|
|||
max_uid = std::cmp::max(max_uid, *uid);
|
||||
tx.execute(
|
||||
"INSERT OR REPLACE INTO envelopes (hash, uid, mailbox_hash, modsequence, envelope) VALUES (?1, ?2, ?3, ?4, ?5)",
|
||||
sqlite3::params![envelope.hash() as i64, *uid as Sqlite3UID, mailbox_hash as i64, modseq, &envelope],
|
||||
sqlite3::params![envelope.hash() as i64, *uid as Sqlite3UID, mailbox_hash.0 as i64, modseq, &envelope],
|
||||
).chain_err_summary(|| format!("Could not insert envelope {} {} in header_cache of account {}", envelope.message_id(), envelope.hash(), uid_store.account_name))?;
|
||||
}
|
||||
}
|
||||
|
@ -486,7 +486,7 @@ mod sqlite3_m {
|
|||
hash_index_lck.remove(&env_hash);
|
||||
tx.execute(
|
||||
"DELETE FROM envelopes WHERE mailbox_hash = ?1 AND uid = ?2;",
|
||||
sqlite3::params![mailbox_hash as i64, *uid as Sqlite3UID],
|
||||
sqlite3::params![mailbox_hash.0 as i64, *uid as Sqlite3UID],
|
||||
)
|
||||
.chain_err_summary(|| {
|
||||
format!(
|
||||
|
@ -502,7 +502,7 @@ mod sqlite3_m {
|
|||
|
||||
let mut ret: Vec<Envelope> = stmt
|
||||
.query_map(
|
||||
sqlite3::params![mailbox_hash as i64, *uid as Sqlite3UID],
|
||||
sqlite3::params![mailbox_hash.0 as i64, *uid as Sqlite3UID],
|
||||
|row| Ok(row.get(0)?),
|
||||
)?
|
||||
.collect::<std::result::Result<_, _>>()?;
|
||||
|
@ -512,7 +512,7 @@ mod sqlite3_m {
|
|||
env.labels_mut().extend(tags.iter().map(|t| tag_hash!(t)));
|
||||
tx.execute(
|
||||
"UPDATE envelopes SET envelope = ?1 WHERE mailbox_hash = ?2 AND uid = ?3;",
|
||||
sqlite3::params![&env, mailbox_hash as i64, *uid as Sqlite3UID],
|
||||
sqlite3::params![&env, mailbox_hash.0 as i64, *uid as Sqlite3UID],
|
||||
)
|
||||
.chain_err_summary(|| {
|
||||
format!(
|
||||
|
@ -556,7 +556,7 @@ mod sqlite3_m {
|
|||
|
||||
let x = stmt
|
||||
.query_map(
|
||||
sqlite3::params![mailbox_hash as i64, uid as Sqlite3UID],
|
||||
sqlite3::params![mailbox_hash.0 as i64, uid as Sqlite3UID],
|
||||
|row| {
|
||||
Ok((
|
||||
row.get(0).map(|u: Sqlite3UID| u as UID)?,
|
||||
|
@ -575,7 +575,7 @@ mod sqlite3_m {
|
|||
|
||||
let x = stmt
|
||||
.query_map(
|
||||
sqlite3::params![mailbox_hash as i64, env_hash as i64],
|
||||
sqlite3::params![mailbox_hash.0 as i64, env_hash as i64],
|
||||
|row| {
|
||||
Ok((
|
||||
row.get(0).map(|u: Sqlite3UID| u as UID)?,
|
||||
|
@ -612,7 +612,7 @@ mod sqlite3_m {
|
|||
)?;
|
||||
let x = stmt
|
||||
.query_map(
|
||||
sqlite3::params![mailbox_hash as i64, uid as Sqlite3UID],
|
||||
sqlite3::params![mailbox_hash.0 as i64, uid as Sqlite3UID],
|
||||
|row| Ok(row.get(0)?),
|
||||
)?
|
||||
.collect::<std::result::Result<_, _>>()?;
|
||||
|
@ -624,7 +624,7 @@ mod sqlite3_m {
|
|||
)?;
|
||||
let x = stmt
|
||||
.query_map(
|
||||
sqlite3::params![mailbox_hash as i64, env_hash as i64],
|
||||
sqlite3::params![mailbox_hash.0 as i64, env_hash as i64],
|
||||
|row| Ok(row.get(0)?),
|
||||
)?
|
||||
.collect::<std::result::Result<_, _>>()?;
|
||||
|
|
|
@ -33,6 +33,7 @@ pub struct ImapMailbox {
|
|||
pub path: String,
|
||||
pub name: String,
|
||||
pub parent: Option<MailboxHash>,
|
||||
pub has_children: bool,
|
||||
pub children: Vec<MailboxHash>,
|
||||
pub separator: u8,
|
||||
pub usage: Arc<RwLock<SpecialUsageMailbox>>,
|
||||
|
@ -47,6 +48,12 @@ pub struct ImapMailbox {
|
|||
}
|
||||
|
||||
impl ImapMailbox {
|
||||
pub fn set_has_children(&mut self, has_children: bool) -> &mut Self {
|
||||
self.has_children = has_children;
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn imap_path(&self) -> &str {
|
||||
&self.imap_path
|
||||
}
|
||||
|
@ -88,6 +95,9 @@ impl BackendMailbox for ImapMailbox {
|
|||
}
|
||||
|
||||
fn children(&self) -> &[MailboxHash] {
|
||||
if !self.has_children {
|
||||
return &[];
|
||||
}
|
||||
&self.children
|
||||
}
|
||||
|
||||
|
@ -106,9 +116,11 @@ impl BackendMailbox for ImapMailbox {
|
|||
fn permissions(&self) -> MailboxPermissions {
|
||||
*self.permissions.lock().unwrap()
|
||||
}
|
||||
|
||||
fn is_subscribed(&self) -> bool {
|
||||
self.is_subscribed
|
||||
}
|
||||
|
||||
fn set_is_subscribed(&mut self, new_val: bool) -> Result<()> {
|
||||
self.is_subscribed = new_val;
|
||||
Ok(())
|
||||
|
|
|
@ -449,6 +449,7 @@ pub fn list_mailbox_result(input: &[u8]) -> IResult<&[u8], ImapMailbox> {
|
|||
({
|
||||
let separator: u8 = separator[0];
|
||||
let mut f = ImapMailbox::default();
|
||||
f.has_children = true;
|
||||
f.no_select = false;
|
||||
f.is_subscribed = false;
|
||||
|
||||
|
@ -475,10 +476,14 @@ pub fn list_mailbox_result(input: &[u8]) -> IResult<&[u8], ImapMailbox> {
|
|||
let _ = f.set_special_usage(SpecialUsageMailbox::Flagged);
|
||||
} else if p.eq_ignore_ascii_case(b"\\Archive") {
|
||||
let _ = f.set_special_usage(SpecialUsageMailbox::Archive);
|
||||
} else if p.eq_ignore_ascii_case(b"\\HasChildren") {
|
||||
f.has_children = true;
|
||||
} else if p.eq_ignore_ascii_case(b"\\HasNoChildren") {
|
||||
f.has_children = false;
|
||||
}
|
||||
}
|
||||
f.imap_path = path.to_string();
|
||||
f.hash = get_path_hash!(&f.imap_path);
|
||||
f.hash = MailboxHash(get_path_hash!(&f.imap_path));
|
||||
f.path = if separator == b'/' {
|
||||
f.imap_path.clone()
|
||||
} else {
|
||||
|
@ -486,7 +491,7 @@ pub fn list_mailbox_result(input: &[u8]) -> IResult<&[u8], ImapMailbox> {
|
|||
};
|
||||
f.name = if let Some(pos) = f.imap_path.as_bytes().iter().rposition(|&c| c == separator)
|
||||
{
|
||||
f.parent = Some(get_path_hash!(&f.imap_path[..pos]));
|
||||
f.parent = Some(MailboxHash(get_path_hash!(&f.imap_path[..pos])));
|
||||
f.imap_path[pos + 1..].to_string()
|
||||
} else {
|
||||
f.imap_path.clone()
|
||||
|
@ -1561,7 +1566,9 @@ pub struct StatusResponse {
|
|||
pub fn status_response(input: &[u8]) -> IResult<&[u8], StatusResponse> {
|
||||
let (input, _) = tag("* STATUS ")(input)?;
|
||||
let (input, mailbox) = take_until(" (")(input)?;
|
||||
let mailbox = mailbox_token(mailbox).map(|(_, m)| get_path_hash!(m)).ok();
|
||||
let mailbox = mailbox_token(mailbox)
|
||||
.map(|(_, m)| MailboxHash(get_path_hash!(m)))
|
||||
.ok();
|
||||
let (input, _) = tag(" (")(input)?;
|
||||
let (input, result) = permutation((
|
||||
opt(preceded(
|
||||
|
|
|
@ -138,8 +138,11 @@ impl MaildirMailbox {
|
|||
settings: &AccountSettings,
|
||||
) -> Result<Self> {
|
||||
let pathbuf = PathBuf::from(&path);
|
||||
let mut h = DefaultHasher::new();
|
||||
pathbuf.hash(&mut h);
|
||||
let hash = {
|
||||
let mut h = DefaultHasher::new();
|
||||
pathbuf.hash(&mut h);
|
||||
MailboxHash(h.finish())
|
||||
};
|
||||
|
||||
/* Check if mailbox path (Eg `INBOX/Lists/luddites`) is included in the subscribed
|
||||
* mailboxes in user configuration */
|
||||
|
@ -159,7 +162,7 @@ impl MaildirMailbox {
|
|||
};
|
||||
|
||||
let ret = MaildirMailbox {
|
||||
hash: h.finish(),
|
||||
hash,
|
||||
name: file_name,
|
||||
path: fname.unwrap().to_path_buf(),
|
||||
fs_path: pathbuf,
|
||||
|
|
|
@ -220,7 +220,7 @@ impl MailBackend for MaildirType {
|
|||
let account_hash = {
|
||||
let mut hasher = DefaultHasher::default();
|
||||
hasher.write(self.name.as_bytes());
|
||||
hasher.finish()
|
||||
AccountHash(hasher.finish())
|
||||
};
|
||||
let sender = self.event_consumer.clone();
|
||||
|
||||
|
@ -327,7 +327,7 @@ impl MailBackend for MaildirType {
|
|||
let account_hash = {
|
||||
let mut hasher = DefaultHasher::default();
|
||||
hasher.write(self.name.as_bytes());
|
||||
hasher.finish()
|
||||
AccountHash(hasher.finish())
|
||||
};
|
||||
let root_path = self.path.to_path_buf();
|
||||
watcher.watch(&root_path, RecursiveMode::Recursive).unwrap();
|
||||
|
@ -377,7 +377,7 @@ impl MailBackend for MaildirType {
|
|||
}
|
||||
};
|
||||
}
|
||||
let mailbox_hash = get_path_hash!(pathbuf);
|
||||
let mailbox_hash = MailboxHash(get_path_hash!(pathbuf));
|
||||
let file_name = pathbuf
|
||||
.as_path()
|
||||
.strip_prefix(&root_path)
|
||||
|
@ -418,7 +418,7 @@ impl MailBackend for MaildirType {
|
|||
/* Update */
|
||||
DebouncedEvent::NoticeWrite(pathbuf) | DebouncedEvent::Write(pathbuf) => {
|
||||
debug!("DebouncedEvent::Write(path = {:?}", &pathbuf);
|
||||
let mailbox_hash = get_path_hash!(pathbuf);
|
||||
let mailbox_hash = MailboxHash(get_path_hash!(pathbuf));
|
||||
let mut hash_indexes_lock = hash_indexes.lock().unwrap();
|
||||
let index_lock =
|
||||
&mut hash_indexes_lock.entry(mailbox_hash).or_default();
|
||||
|
@ -495,7 +495,7 @@ impl MailBackend for MaildirType {
|
|||
/* Remove */
|
||||
DebouncedEvent::NoticeRemove(pathbuf) | DebouncedEvent::Remove(pathbuf) => {
|
||||
debug!("DebouncedEvent::Remove(path = {:?}", pathbuf);
|
||||
let mailbox_hash = get_path_hash!(pathbuf);
|
||||
let mailbox_hash = MailboxHash(get_path_hash!(pathbuf));
|
||||
let mut hash_indexes_lock = hash_indexes.lock().unwrap();
|
||||
let index_lock = hash_indexes_lock.entry(mailbox_hash).or_default();
|
||||
let hash: EnvelopeHash = if let Some((k, _)) =
|
||||
|
@ -549,9 +549,9 @@ impl MailBackend for MaildirType {
|
|||
/* Envelope hasn't changed */
|
||||
DebouncedEvent::Rename(src, dest) => {
|
||||
debug!("DebouncedEvent::Rename(src = {:?}, dest = {:?})", src, dest);
|
||||
let mailbox_hash = get_path_hash!(src);
|
||||
let mailbox_hash = MailboxHash(get_path_hash!(src));
|
||||
let dest_mailbox = {
|
||||
let dest_mailbox = get_path_hash!(dest);
|
||||
let dest_mailbox = MailboxHash(get_path_hash!(dest));
|
||||
if dest_mailbox == mailbox_hash {
|
||||
None
|
||||
} else {
|
||||
|
@ -787,7 +787,7 @@ impl MailBackend for MaildirType {
|
|||
/* Maybe a re-read should be triggered here just to be safe.
|
||||
(sender)(account_hash, BackendEvent::Refresh(RefreshEvent {
|
||||
account_hash,
|
||||
mailbox_hash: get_path_hash!(dest),
|
||||
mailbox_hash: MailboxHash(get_path_hash!(dest)),
|
||||
kind: Rescan,
|
||||
}));
|
||||
*/
|
||||
|
@ -1011,7 +1011,7 @@ impl MailBackend for MaildirType {
|
|||
.map(|item| *item.0)
|
||||
});
|
||||
|
||||
let mailbox_hash = get_path_hash!(&path);
|
||||
let mailbox_hash = MailboxHash(get_path_hash!(&path));
|
||||
if let Some(parent) = parent {
|
||||
self.mailboxes
|
||||
.entry(parent)
|
||||
|
|
|
@ -981,7 +981,7 @@ impl MailBackend for MboxType {
|
|||
let account_hash = {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
hasher.write(self.account_name.as_bytes());
|
||||
hasher.finish()
|
||||
AccountHash(hasher.finish())
|
||||
};
|
||||
let mailboxes = self.mailboxes.clone();
|
||||
let mailbox_index = self.mailbox_index.clone();
|
||||
|
@ -1002,7 +1002,7 @@ impl MailBackend for MboxType {
|
|||
Ok(event) => match event {
|
||||
/* Update */
|
||||
DebouncedEvent::NoticeWrite(pathbuf) | DebouncedEvent::Write(pathbuf) => {
|
||||
let mailbox_hash = get_path_hash!(&pathbuf);
|
||||
let mailbox_hash = MailboxHash(get_path_hash!(&pathbuf));
|
||||
let file = match std::fs::OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
|
@ -1064,7 +1064,7 @@ impl MailBackend for MboxType {
|
|||
.values()
|
||||
.any(|f| f.fs_path == pathbuf)
|
||||
{
|
||||
let mailbox_hash = get_path_hash!(&pathbuf);
|
||||
let mailbox_hash = MailboxHash(get_path_hash!(&pathbuf));
|
||||
(sender)(
|
||||
account_hash,
|
||||
BackendEvent::Refresh(RefreshEvent {
|
||||
|
@ -1081,7 +1081,7 @@ impl MailBackend for MboxType {
|
|||
}
|
||||
DebouncedEvent::Rename(src, dest) => {
|
||||
if mailboxes.lock().unwrap().values().any(|f| f.fs_path == src) {
|
||||
let mailbox_hash = get_path_hash!(&src);
|
||||
let mailbox_hash = MailboxHash(get_path_hash!(&src));
|
||||
(sender)(
|
||||
account_hash,
|
||||
BackendEvent::Refresh(RefreshEvent {
|
||||
|
@ -1265,7 +1265,7 @@ impl MboxType {
|
|||
.file_name()
|
||||
.map(|f| f.to_string_lossy().into())
|
||||
.unwrap_or_default();
|
||||
let hash = get_path_hash!(&ret.path);
|
||||
let hash = MailboxHash(get_path_hash!(&ret.path));
|
||||
|
||||
let read_only = if let Ok(metadata) = std::fs::metadata(&ret.path) {
|
||||
metadata.permissions().readonly()
|
||||
|
@ -1303,7 +1303,7 @@ impl MboxType {
|
|||
/* Look for other mailboxes */
|
||||
for (k, f) in s.mailboxes.iter() {
|
||||
if let Some(path_str) = f.extra.get("path") {
|
||||
let hash = get_path_hash!(path_str);
|
||||
let hash = MailboxHash(get_path_hash!(path_str));
|
||||
let pathbuf: PathBuf = path_str.into();
|
||||
if !pathbuf.exists() || pathbuf.is_dir() {
|
||||
return Err(MeliError::new(format!(
|
||||
|
|
|
@ -573,12 +573,12 @@ impl NntpType {
|
|||
let account_hash = {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
hasher.write(s.name.as_bytes());
|
||||
hasher.finish()
|
||||
AccountHash(hasher.finish())
|
||||
};
|
||||
let account_name = Arc::new(s.name().to_string());
|
||||
let mut mailboxes = HashMap::default();
|
||||
for (k, _f) in s.mailboxes.iter() {
|
||||
let mailbox_hash = get_path_hash!(&k);
|
||||
let mailbox_hash = MailboxHash(get_path_hash!(&k));
|
||||
mailboxes.insert(
|
||||
mailbox_hash,
|
||||
NntpMailbox {
|
||||
|
@ -645,7 +645,7 @@ impl NntpType {
|
|||
if s.len() != 3 {
|
||||
continue;
|
||||
}
|
||||
let mailbox_hash = get_path_hash!(&s[0]);
|
||||
let mailbox_hash = MailboxHash(get_path_hash!(&s[0]));
|
||||
mailboxes_lck.entry(mailbox_hash).and_modify(|m| {
|
||||
*m.high_watermark.lock().unwrap() = usize::from_str(s[1]).unwrap_or(0);
|
||||
*m.low_watermark.lock().unwrap() = usize::from_str(s[2]).unwrap_or(0);
|
||||
|
|
|
@ -367,7 +367,7 @@ impl NotmuchDb {
|
|||
let hash = {
|
||||
let mut h = DefaultHasher::new();
|
||||
k.hash(&mut h);
|
||||
h.finish()
|
||||
MailboxHash(h.finish())
|
||||
};
|
||||
mailboxes.insert(
|
||||
hash,
|
||||
|
@ -396,7 +396,7 @@ impl NotmuchDb {
|
|||
let account_hash = {
|
||||
let mut hasher = DefaultHasher::new();
|
||||
hasher.write(s.name().as_bytes());
|
||||
hasher.finish()
|
||||
AccountHash(hasher.finish())
|
||||
};
|
||||
Ok(Box::new(NotmuchDb {
|
||||
lib,
|
||||
|
@ -533,7 +533,7 @@ impl MailBackend for NotmuchDb {
|
|||
database: Arc<DbConnection>,
|
||||
index: Arc<RwLock<HashMap<EnvelopeHash, CString>>>,
|
||||
mailbox_index: Arc<RwLock<HashMap<EnvelopeHash, SmallVec<[MailboxHash; 16]>>>>,
|
||||
mailboxes: Arc<RwLock<HashMap<u64, NotmuchMailbox>>>,
|
||||
mailboxes: Arc<RwLock<HashMap<MailboxHash, NotmuchMailbox>>>,
|
||||
tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
|
||||
iter: std::vec::IntoIter<CString>,
|
||||
}
|
||||
|
|
|
@ -195,6 +195,8 @@ pub struct SmtpExtensionSupport {
|
|||
pipelining: bool,
|
||||
#[serde(default = "crate::conf::true_val")]
|
||||
chunking: bool,
|
||||
#[serde(default = "crate::conf::true_val")]
|
||||
_8bitmime: bool,
|
||||
//Essentially, the PRDR extension to SMTP allows (but does not require) an SMTP server to
|
||||
//issue multiple responses after a message has been transferred, by mutual consent of the
|
||||
//client and server. SMTP clients that support the PRDR extension then use the expanded
|
||||
|
@ -202,7 +204,7 @@ pub struct SmtpExtensionSupport {
|
|||
//envelope exchange.
|
||||
#[serde(default = "crate::conf::true_val")]
|
||||
prdr: bool,
|
||||
#[serde(default = "crate::conf::false_val")]
|
||||
#[serde(default = "crate::conf::true_val")]
|
||||
binarymime: bool,
|
||||
//Resources:
|
||||
//- http://www.postfix.org/SMTPUTF8_README.html
|
||||
|
@ -224,7 +226,8 @@ impl Default for SmtpExtensionSupport {
|
|||
pipelining: true,
|
||||
chunking: true,
|
||||
prdr: true,
|
||||
binarymime: false,
|
||||
_8bitmime: true,
|
||||
binarymime: true,
|
||||
smtputf8: true,
|
||||
auth: true,
|
||||
dsn_notify: Some("FAILURE".into()),
|
||||
|
@ -561,6 +564,7 @@ impl SmtpConnection {
|
|||
self.server_conf.extensions.pipelining &= reply.lines.contains(&"PIPELINING");
|
||||
self.server_conf.extensions.chunking &= reply.lines.contains(&"CHUNKING");
|
||||
self.server_conf.extensions.prdr &= reply.lines.contains(&"PRDR");
|
||||
self.server_conf.extensions._8bitmime &= reply.lines.contains(&"8BITMIME");
|
||||
self.server_conf.extensions.binarymime &= reply.lines.contains(&"BINARYMIME");
|
||||
self.server_conf.extensions.smtputf8 &= reply.lines.contains(&"SMTPUTF8");
|
||||
if !reply.lines.contains(&"DSN") {
|
||||
|
@ -637,6 +641,11 @@ impl SmtpConnection {
|
|||
if self.server_conf.extensions.prdr {
|
||||
current_command.push(b" PRDR");
|
||||
}
|
||||
if self.server_conf.extensions.binarymime {
|
||||
current_command.push(b" BODY=BINARYMIME");
|
||||
} else if self.server_conf.extensions._8bitmime {
|
||||
current_command.push(b" BODY=8BITMIME");
|
||||
}
|
||||
self.send_command(¤t_command).await?;
|
||||
current_command.clear();
|
||||
if !self.server_conf.extensions.pipelining {
|
||||
|
@ -681,71 +690,81 @@ impl SmtpConnection {
|
|||
//permitted on either side of the colon following FROM in the MAIL command or TO in the
|
||||
//RCPT command. The syntax is exactly as given above.
|
||||
|
||||
//The third step in the procedure is the DATA command
|
||||
//(or some alternative specified in a service extension).
|
||||
//DATA <CRLF>
|
||||
self.send_command(&[b"DATA"]).await?;
|
||||
//Client SMTP implementations that employ pipelining MUST check ALL statuses associated
|
||||
//with each command in a group. For example, if none of the RCPT TO recipient addresses
|
||||
//were accepted the client must then check the response to the DATA command -- the client
|
||||
//cannot assume that the DATA command will be rejected just because none of the RCPT TO
|
||||
//commands worked. If the DATA command was properly rejected the client SMTP can just
|
||||
//issue RSET, but if the DATA command was accepted the client SMTP should send a single
|
||||
//dot.
|
||||
let mut _all_error = self.server_conf.extensions.pipelining;
|
||||
let mut _any_error = false;
|
||||
let mut ignore_mailfrom = true;
|
||||
for expected_reply_code in pipelining_queue {
|
||||
let reply = self.read_lines(&mut res, expected_reply_code).await?;
|
||||
if !ignore_mailfrom {
|
||||
_all_error &= reply.code.is_err();
|
||||
_any_error |= reply.code.is_err();
|
||||
if self.server_conf.extensions.binarymime {
|
||||
let mail_length = format!("{}", mail.as_bytes().len());
|
||||
self.send_command(&[b"BDAT", mail_length.as_bytes(), b"LAST"])
|
||||
.await?;
|
||||
self.stream
|
||||
.write_all(mail.as_bytes())
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
} else {
|
||||
//The third step in the procedure is the DATA command
|
||||
//(or some alternative specified in a service extension).
|
||||
//DATA <CRLF>
|
||||
self.send_command(&[b"DATA"]).await?;
|
||||
//Client SMTP implementations that employ pipelining MUST check ALL statuses associated
|
||||
//with each command in a group. For example, if none of the RCPT TO recipient addresses
|
||||
//were accepted the client must then check the response to the DATA command -- the client
|
||||
//cannot assume that the DATA command will be rejected just because none of the RCPT TO
|
||||
//commands worked. If the DATA command was properly rejected the client SMTP can just
|
||||
//issue RSET, but if the DATA command was accepted the client SMTP should send a single
|
||||
//dot.
|
||||
let mut _all_error = self.server_conf.extensions.pipelining;
|
||||
let mut _any_error = false;
|
||||
let mut ignore_mailfrom = true;
|
||||
for expected_reply_code in pipelining_queue {
|
||||
let reply = self.read_lines(&mut res, expected_reply_code).await?;
|
||||
if !ignore_mailfrom {
|
||||
_all_error &= reply.code.is_err();
|
||||
_any_error |= reply.code.is_err();
|
||||
}
|
||||
ignore_mailfrom = false;
|
||||
pipelining_results.push(reply.into());
|
||||
}
|
||||
ignore_mailfrom = false;
|
||||
pipelining_results.push(reply.into());
|
||||
}
|
||||
|
||||
//If accepted, the SMTP server returns a 354 Intermediate reply and considers all
|
||||
//succeeding lines up to but not including the end of mail data indicator to be the
|
||||
//message text. When the end of text is successfully received and stored, the
|
||||
//SMTP-receiver sends a "250 OK" reply.
|
||||
self.read_lines(&mut res, Some((ReplyCode::_354, &[])))
|
||||
.await?;
|
||||
//If accepted, the SMTP server returns a 354 Intermediate reply and considers all
|
||||
//succeeding lines up to but not including the end of mail data indicator to be the
|
||||
//message text. When the end of text is successfully received and stored, the
|
||||
//SMTP-receiver sends a "250 OK" reply.
|
||||
self.read_lines(&mut res, Some((ReplyCode::_354, &[])))
|
||||
.await?;
|
||||
|
||||
//Before sending a line of mail text, the SMTP client checks the first character of the
|
||||
//line.If it is a period, one additional period is inserted at the beginning of the line.
|
||||
for line in mail.lines() {
|
||||
if line.starts_with('.') {
|
||||
//Before sending a line of mail text, the SMTP client checks the first character of the
|
||||
//line.If it is a period, one additional period is inserted at the beginning of the line.
|
||||
for line in mail.lines() {
|
||||
if line.starts_with('.') {
|
||||
self.stream
|
||||
.write_all(b".")
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
}
|
||||
self.stream
|
||||
.write_all(b".")
|
||||
.write_all(line.as_bytes())
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
self.stream
|
||||
.write_all(b"\r\n")
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
}
|
||||
self.stream
|
||||
.write_all(line.as_bytes())
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
self.stream
|
||||
.write_all(b"\r\n")
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
}
|
||||
|
||||
if !mail.ends_with('\n') {
|
||||
if !mail.ends_with('\n') {
|
||||
self.stream
|
||||
.write_all(b".\r\n")
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
}
|
||||
|
||||
//The mail data are terminated by a line containing only a period, that is, the character
|
||||
//sequence "<CRLF>.<CRLF>", where the first <CRLF> is actually the terminator of the
|
||||
//previous line (see Section 4.5.2). This is the end of mail data indication.
|
||||
self.stream
|
||||
.write_all(b".\r\n")
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
}
|
||||
|
||||
//The mail data are terminated by a line containing only a period, that is, the character
|
||||
//sequence "<CRLF>.<CRLF>", where the first <CRLF> is actually the terminator of the
|
||||
//previous line (see Section 4.5.2). This is the end of mail data indication.
|
||||
self.stream
|
||||
.write_all(b".\r\n")
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Network)?;
|
||||
|
||||
//The end of mail data indicator also confirms the mail transaction and tells the SMTP
|
||||
//server to now process the stored recipients and mail data. If accepted, the SMTP
|
||||
//server returns a "250 OK" reply.
|
||||
|
|
|
@ -151,7 +151,7 @@ impl Composer {
|
|||
pager.set_show_scrollbar(true);
|
||||
Composer {
|
||||
reply_context: None,
|
||||
account_hash: 0,
|
||||
account_hash: AccountHash(0),
|
||||
cursor: Cursor::Headers,
|
||||
pager,
|
||||
draft: Draft::default(),
|
||||
|
|
|
@ -613,6 +613,7 @@ pub struct Listing {
|
|||
cursor_pos: (usize, MenuEntryCursor),
|
||||
menu_cursor_pos: (usize, MenuEntryCursor),
|
||||
menu_content: CellBuffer,
|
||||
menu_content_dirty: bool,
|
||||
menu_scrollbar_show_timer: crate::jobs::Timer,
|
||||
show_menu_scrollbar: ShowMenuScrollbar,
|
||||
startup_checks_rate: RateLimit,
|
||||
|
@ -698,7 +699,8 @@ impl Component for Listing {
|
|||
match self.component {
|
||||
ListingComponent::Offline(_) => {}
|
||||
_ => {
|
||||
self.component = Offline(OfflineListing::new((account_hash, 0)));
|
||||
self.component =
|
||||
Offline(OfflineListing::new((account_hash, MailboxHash(0))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -720,7 +722,8 @@ impl Component for Listing {
|
|||
match self.component {
|
||||
ListingComponent::Offline(_) => {}
|
||||
_ => {
|
||||
self.component = Offline(OfflineListing::new((account_hash, 0)));
|
||||
self.component =
|
||||
Offline(OfflineListing::new((account_hash, MailboxHash(0))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -743,6 +746,7 @@ impl Component for Listing {
|
|||
*account_settings!(context[account_hash].listing.sidebar_divider);
|
||||
self.sidebar_divider_theme = conf::value(context, "mail.sidebar_divider");
|
||||
self.menu_content = CellBuffer::new_with_context(0, 0, None, context);
|
||||
self.menu_content_dirty = true;
|
||||
self.set_dirty(true);
|
||||
}
|
||||
UIEvent::Timer(n) if *n == self.menu_scrollbar_show_timer.id() => {
|
||||
|
@ -750,6 +754,7 @@ impl Component for Listing {
|
|||
self.show_menu_scrollbar = ShowMenuScrollbar::False;
|
||||
self.set_dirty(true);
|
||||
self.menu_content.empty();
|
||||
self.menu_content_dirty = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -775,41 +780,10 @@ impl Component for Listing {
|
|||
if self.cursor_pos.0 == account_index {
|
||||
self.change_account(context);
|
||||
} else {
|
||||
let previous_collapsed_mailboxes: BTreeSet<MailboxHash> = self.accounts
|
||||
[account_index]
|
||||
.entries
|
||||
.iter()
|
||||
.filter_map(|e| {
|
||||
if e.collapsed {
|
||||
Some(e.mailbox_hash)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<_>();
|
||||
self.accounts[account_index].entries = context.accounts[&*account_hash]
|
||||
.list_mailboxes()
|
||||
.into_iter()
|
||||
.filter(|mailbox_node| {
|
||||
context.accounts[&*account_hash][&mailbox_node.hash]
|
||||
.ref_mailbox
|
||||
.is_subscribed()
|
||||
})
|
||||
.map(|f| MailboxMenuEntry {
|
||||
depth: f.depth,
|
||||
indentation: f.indentation,
|
||||
has_sibling: f.has_sibling,
|
||||
mailbox_hash: f.hash,
|
||||
visible: true,
|
||||
collapsed: if previous_collapsed_mailboxes.is_empty() {
|
||||
context.accounts[&*account_hash][&f.hash].conf.collapsed
|
||||
} else {
|
||||
previous_collapsed_mailboxes.contains(&f.hash)
|
||||
},
|
||||
})
|
||||
.collect::<_>();
|
||||
self.update_menu_account(account_index, context);
|
||||
self.set_dirty(true);
|
||||
self.menu_content.empty();
|
||||
self.menu_content_dirty = true;
|
||||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||
|
@ -825,35 +799,8 @@ impl Component for Listing {
|
|||
.get_index_of(account_hash)
|
||||
.expect("Invalid account_hash in UIEventMailbox{Delete,Create}");
|
||||
self.menu_content.empty();
|
||||
let previous_collapsed_mailboxes: BTreeSet<MailboxHash> = self.accounts
|
||||
[account_index]
|
||||
.entries
|
||||
.iter()
|
||||
.filter_map(|e| {
|
||||
if e.collapsed {
|
||||
Some(e.mailbox_hash)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<_>();
|
||||
self.accounts[account_index].entries = context.accounts[&*account_hash]
|
||||
.list_mailboxes()
|
||||
.into_iter()
|
||||
.filter(|mailbox_node| {
|
||||
context.accounts[&*account_hash][&mailbox_node.hash]
|
||||
.ref_mailbox
|
||||
.is_subscribed()
|
||||
})
|
||||
.map(|f| MailboxMenuEntry {
|
||||
depth: f.depth,
|
||||
indentation: f.indentation,
|
||||
has_sibling: f.has_sibling,
|
||||
mailbox_hash: f.hash,
|
||||
visible: true,
|
||||
collapsed: previous_collapsed_mailboxes.contains(&f.hash),
|
||||
})
|
||||
.collect::<_>();
|
||||
self.menu_content_dirty = true;
|
||||
self.update_menu_account(account_index, context);
|
||||
let mut fallback = 0;
|
||||
if let MenuEntryCursor::Mailbox(ref mut cur) = self.cursor_pos.1 {
|
||||
*cur = std::cmp::min(
|
||||
|
@ -900,6 +847,7 @@ impl Component for Listing {
|
|||
self.component
|
||||
.set_coordinates((account_hash, *mailbox_hash));
|
||||
self.menu_content.empty();
|
||||
self.menu_content_dirty = true;
|
||||
self.set_dirty(true);
|
||||
}
|
||||
return true;
|
||||
|
@ -1378,6 +1326,7 @@ impl Component for Listing {
|
|||
target.collapsed = !(target.collapsed);
|
||||
self.dirty = true;
|
||||
self.menu_content.empty();
|
||||
self.menu_content_dirty = true;
|
||||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::ScrollUpdate(
|
||||
|
@ -1520,6 +1469,7 @@ impl Component for Listing {
|
|||
self.show_menu_scrollbar = ShowMenuScrollbar::True;
|
||||
}
|
||||
self.menu_content.empty();
|
||||
self.menu_content_dirty = true;
|
||||
self.set_dirty(true);
|
||||
return true;
|
||||
}
|
||||
|
@ -1581,6 +1531,7 @@ impl Component for Listing {
|
|||
self.show_menu_scrollbar = ShowMenuScrollbar::True;
|
||||
}
|
||||
self.menu_content.empty();
|
||||
self.menu_content_dirty = true;
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(ref k)
|
||||
|
@ -1634,6 +1585,7 @@ impl Component for Listing {
|
|||
self.show_menu_scrollbar = ShowMenuScrollbar::True;
|
||||
}
|
||||
self.menu_content.empty();
|
||||
self.menu_content_dirty = true;
|
||||
self.set_dirty(true);
|
||||
|
||||
return true;
|
||||
|
@ -1660,6 +1612,7 @@ impl Component for Listing {
|
|||
self.dirty = true;
|
||||
/* clear menu to force redraw */
|
||||
self.menu_content.empty();
|
||||
self.menu_content_dirty = true;
|
||||
context
|
||||
.replies
|
||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||
|
@ -1810,13 +1763,14 @@ impl Listing {
|
|||
.collect();
|
||||
let first_account_hash = account_entries[0].hash;
|
||||
let mut ret = Listing {
|
||||
component: Offline(OfflineListing::new((first_account_hash, 0))),
|
||||
component: Offline(OfflineListing::new((first_account_hash, MailboxHash(0)))),
|
||||
accounts: account_entries,
|
||||
status: None,
|
||||
dirty: true,
|
||||
cursor_pos: (0, MenuEntryCursor::Mailbox(0)),
|
||||
menu_cursor_pos: (0, MenuEntryCursor::Mailbox(0)),
|
||||
menu_content: CellBuffer::new_with_context(0, 0, None, context),
|
||||
menu_content_dirty: true,
|
||||
menu_scrollbar_show_timer: context.job_executor.clone().create_timer(
|
||||
std::time::Duration::from_secs(0),
|
||||
std::time::Duration::from_millis(1200),
|
||||
|
@ -1841,6 +1795,11 @@ impl Listing {
|
|||
}
|
||||
|
||||
fn draw_menu(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||
if self.menu_content_dirty {
|
||||
for account_index in 0..self.accounts.len() {
|
||||
self.update_menu_account(account_index, context);
|
||||
}
|
||||
}
|
||||
clear_area(grid, area, self.theme_default);
|
||||
let total_height: usize = 3 * (self.accounts.len())
|
||||
+ self
|
||||
|
@ -2283,9 +2242,8 @@ impl Listing {
|
|||
}
|
||||
}
|
||||
|
||||
fn change_account(&mut self, context: &mut Context) {
|
||||
let account_hash = context.accounts[self.cursor_pos.0].hash();
|
||||
let previous_collapsed_mailboxes: BTreeSet<MailboxHash> = self.accounts[self.cursor_pos.0]
|
||||
fn update_menu_account(&mut self, account_index: usize, context: &mut Context) {
|
||||
let previous_collapsed_mailboxes: BTreeSet<MailboxHash> = self.accounts[account_index]
|
||||
.entries
|
||||
.iter()
|
||||
.filter_map(|e| {
|
||||
|
@ -2296,11 +2254,11 @@ impl Listing {
|
|||
}
|
||||
})
|
||||
.collect::<_>();
|
||||
self.accounts[self.cursor_pos.0].entries = context.accounts[self.cursor_pos.0]
|
||||
self.accounts[account_index].entries = context.accounts[account_index]
|
||||
.list_mailboxes()
|
||||
.into_iter()
|
||||
.filter(|mailbox_node| {
|
||||
context.accounts[self.cursor_pos.0][&mailbox_node.hash]
|
||||
context.accounts[account_index][&mailbox_node.hash]
|
||||
.ref_mailbox
|
||||
.is_subscribed()
|
||||
})
|
||||
|
@ -2311,12 +2269,17 @@ impl Listing {
|
|||
mailbox_hash: f.hash,
|
||||
visible: true,
|
||||
collapsed: if previous_collapsed_mailboxes.is_empty() {
|
||||
context.accounts[self.cursor_pos.0][&f.hash].conf.collapsed
|
||||
context.accounts[account_index][&f.hash].conf.collapsed
|
||||
} else {
|
||||
previous_collapsed_mailboxes.contains(&f.hash)
|
||||
},
|
||||
})
|
||||
.collect::<_>();
|
||||
}
|
||||
|
||||
fn change_account(&mut self, context: &mut Context) {
|
||||
self.update_menu_account(self.cursor_pos.0, context);
|
||||
let account_hash = self.accounts[self.cursor_pos.0].hash;
|
||||
match self.cursor_pos.1 {
|
||||
MenuEntryCursor::Mailbox(idx) => {
|
||||
/* Account might have no mailboxes yet if it's offline */
|
||||
|
@ -2334,7 +2297,7 @@ impl Listing {
|
|||
self.component.set_style(*index_style);
|
||||
} else {
|
||||
/* Set to dummy */
|
||||
self.component = Offline(OfflineListing::new((account_hash, 0)));
|
||||
self.component = Offline(OfflineListing::new((account_hash, MailboxHash(0))));
|
||||
}
|
||||
self.status = None;
|
||||
context
|
||||
|
|
|
@ -849,7 +849,7 @@ impl CompactListing {
|
|||
pub const DESCRIPTION: &'static str = "compact listing";
|
||||
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Box<Self> {
|
||||
Box::new(CompactListing {
|
||||
cursor_pos: (coordinates.0, 1, 0),
|
||||
cursor_pos: (coordinates.0, MailboxHash(0), 0),
|
||||
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
||||
length: 0,
|
||||
sort: (Default::default(), Default::default()),
|
||||
|
|
|
@ -570,7 +570,7 @@ impl ConversationsListing {
|
|||
|
||||
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Box<Self> {
|
||||
Box::new(ConversationsListing {
|
||||
cursor_pos: (coordinates.0, 1, 0),
|
||||
cursor_pos: (coordinates.0, MailboxHash(0), 0),
|
||||
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
||||
length: 0,
|
||||
sort: (Default::default(), Default::default()),
|
||||
|
|
|
@ -660,7 +660,7 @@ impl PlainListing {
|
|||
const DESCRIPTION: &'static str = "plain listing";
|
||||
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Box<Self> {
|
||||
Box::new(PlainListing {
|
||||
cursor_pos: (0, 1, 0),
|
||||
cursor_pos: (AccountHash(0), MailboxHash(0), 0),
|
||||
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
||||
length: 0,
|
||||
sort: (Default::default(), Default::default()),
|
||||
|
|
|
@ -759,7 +759,7 @@ impl fmt::Display for ThreadListing {
|
|||
impl ThreadListing {
|
||||
pub fn new(coordinates: (AccountHash, MailboxHash)) -> Box<Self> {
|
||||
Box::new(ThreadListing {
|
||||
cursor_pos: (coordinates.0, 0, 0),
|
||||
cursor_pos: (coordinates.0, MailboxHash(0), 0),
|
||||
new_cursor_pos: (coordinates.0, coordinates.1, 0),
|
||||
length: 0,
|
||||
sort: (Default::default(), Default::default()),
|
||||
|
|
|
@ -1652,7 +1652,7 @@ impl Component for MailView {
|
|||
}
|
||||
|
||||
fn process_event(&mut self, mut event: &mut UIEvent, context: &mut Context) -> bool {
|
||||
if self.coordinates.0 == 0 || self.coordinates.1 == 0 {
|
||||
if self.coordinates.0 .0 == 0 || self.coordinates.1 .0 == 0 {
|
||||
return false;
|
||||
}
|
||||
let shortcuts = self.get_shortcuts(context);
|
||||
|
|
|
@ -1112,7 +1112,7 @@ impl Account {
|
|||
}
|
||||
|
||||
pub fn load(&mut self, mailbox_hash: MailboxHash) -> result::Result<(), usize> {
|
||||
if mailbox_hash == 0 {
|
||||
if mailbox_hash.0 == 0 {
|
||||
return Err(0);
|
||||
}
|
||||
match self.mailbox_entries[&mailbox_hash].status {
|
||||
|
|
|
@ -314,7 +314,12 @@ fn run_app(opt: Opt) -> Result<()> {
|
|||
sender,
|
||||
receiver.clone(),
|
||||
)?;
|
||||
state.register_component(Box::new(EnvelopeView::new(wrapper, None, None, 0)));
|
||||
state.register_component(Box::new(EnvelopeView::new(
|
||||
wrapper,
|
||||
None,
|
||||
None,
|
||||
AccountHash(0),
|
||||
)));
|
||||
} else {
|
||||
state = State::new(None, sender, receiver.clone())?;
|
||||
#[cfg(feature = "svgscreenshot")]
|
||||
|
|
|
@ -258,7 +258,7 @@ impl State {
|
|||
use std::hash::Hasher;
|
||||
let mut hasher = DefaultHasher::new();
|
||||
hasher.write(n.as_bytes());
|
||||
hasher.finish()
|
||||
AccountHash(hasher.finish())
|
||||
};
|
||||
Account::new(
|
||||
account_hash,
|
||||
|
|
Loading…
Add table
Reference in a new issue