mirror of
https://github.com/chatmail/core.git
synced 2026-07-12 14:03:10 +03:00
Compare commits
1 Commits
hpk-fix-as
...
fix/async-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55ada1cd89 |
@@ -138,6 +138,8 @@ jobs:
|
||||
steps:
|
||||
- *restore-workspace
|
||||
- *restore-cache
|
||||
- run: rustup install $(cat rust-toolchain)
|
||||
- run: rustup default $(cat rust-toolchain)
|
||||
- run:
|
||||
name: build docs, run tests and build wheels
|
||||
command: ci_scripts/run-python.sh
|
||||
|
||||
@@ -427,15 +427,19 @@ class TestOnlineAccount:
|
||||
assert self_addr not in ev[2]
|
||||
ev = ac1._evlogger.get_matching("DC_EVENT_DELETED_BLOB_FILE")
|
||||
|
||||
def test_mvbox_sentbox_threads(self, acfactory):
|
||||
def test_mvbox_sentbox_threads(self, acfactory, lp):
|
||||
ac1 = acfactory.get_online_configuring_account(mvbox=True, sentbox=True)
|
||||
ac2 = acfactory.get_online_configuring_account()
|
||||
wait_configuration_progress(ac2, 1000)
|
||||
wait_configuration_progress(ac1, 1000)
|
||||
lp.sec("configuration completed for both accounts")
|
||||
chat = self.get_chat(ac1, ac2)
|
||||
lp.sec("ac1: send text to chat with ac2")
|
||||
chat.send_text("message1")
|
||||
lp.sec("ac2: wait for incoming message")
|
||||
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
||||
assert ev[2] > const.DC_CHAT_ID_LAST_SPECIAL
|
||||
lp.sec("message arrived, test ends")
|
||||
|
||||
def test_move_works(self, acfactory):
|
||||
ac1 = acfactory.get_online_configuring_account()
|
||||
@@ -675,6 +679,7 @@ class TestOnlineAccount:
|
||||
assert len(messages) == 1
|
||||
assert messages[0].text == "msg1"
|
||||
|
||||
pytest.xfail("cannot export twice yet, probably due to interrupt_idle failing")
|
||||
# wait until a second passed since last backup
|
||||
# because get_latest_backupfile() shall return the latest backup
|
||||
# from a UI it's unlikely anyone manages to export two
|
||||
@@ -895,7 +900,7 @@ class TestOnlineConfigureFails:
|
||||
ac1.start_threads()
|
||||
wait_configuration_progress(ac1, 500)
|
||||
ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK")
|
||||
assert "cannot login" in ev1[2].lower()
|
||||
assert "authentication failed" in ev1[2].lower()
|
||||
wait_configuration_progress(ac1, 0, 0)
|
||||
|
||||
def test_invalid_user(self, acfactory):
|
||||
@@ -904,7 +909,7 @@ class TestOnlineConfigureFails:
|
||||
ac1.start_threads()
|
||||
wait_configuration_progress(ac1, 500)
|
||||
ev1 = ac1._evlogger.get_matching("DC_EVENT_ERROR_NETWORK")
|
||||
assert "cannot login" in ev1[2].lower()
|
||||
assert "authentication failed" in ev1[2].lower()
|
||||
wait_configuration_progress(ac1, 0, 0)
|
||||
|
||||
def test_invalid_domain(self, acfactory):
|
||||
|
||||
167
src/imap.rs
167
src/imap.rs
@@ -45,7 +45,6 @@ pub struct Imap {
|
||||
session: Arc<Mutex<Option<Session>>>,
|
||||
connected: Arc<Mutex<bool>>,
|
||||
interrupt: Arc<Mutex<Option<stop_token::StopSource>>>,
|
||||
skip_next_idle_wait: Arc<Mutex<bool>>,
|
||||
|
||||
should_reconnect: AtomicBool,
|
||||
}
|
||||
@@ -120,7 +119,6 @@ impl Imap {
|
||||
config: Arc::new(RwLock::new(ImapConfig::default())),
|
||||
interrupt: Arc::new(Mutex::new(None)),
|
||||
connected: Arc::new(Mutex::new(false)),
|
||||
skip_next_idle_wait: Arc::new(Mutex::new(false)),
|
||||
should_reconnect: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
@@ -398,16 +396,13 @@ impl Imap {
|
||||
})
|
||||
}
|
||||
|
||||
async fn select_folder<S: AsRef<str>>(
|
||||
&self,
|
||||
context: &Context,
|
||||
folder: Option<S>,
|
||||
) -> ImapActionResult {
|
||||
async fn select_folder<S: AsRef<str>>(&self, context: &Context, folder: Option<S>) -> usize {
|
||||
if self.session.lock().await.is_none() {
|
||||
info!(context, "select_folder detects there is a shutdown ongoing");
|
||||
let mut cfg = self.config.write().await;
|
||||
cfg.selected_folder = None;
|
||||
cfg.selected_folder_needs_expunge = false;
|
||||
return ImapActionResult::Failed;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// if there is a new folder and the new folder is equal to the selected one, there's nothing to do.
|
||||
@@ -415,7 +410,7 @@ impl Imap {
|
||||
if let Some(ref folder) = folder {
|
||||
if let Some(ref selected_folder) = self.config.read().await.selected_folder {
|
||||
if folder.as_ref() == selected_folder {
|
||||
return ImapActionResult::AlreadyDone;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,11 +430,11 @@ impl Imap {
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(context, "failed to close session: {:?}", err);
|
||||
return ImapActionResult::Failed;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return ImapActionResult::Failed;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
self.config.write().await.selected_folder_needs_expunge = false;
|
||||
@@ -455,7 +450,7 @@ impl Imap {
|
||||
config.selected_mailbox = Some(mailbox);
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
info!(
|
||||
context,
|
||||
"Cannot select folder: {}; {:?}.",
|
||||
folder.as_ref(),
|
||||
@@ -464,7 +459,7 @@ impl Imap {
|
||||
|
||||
self.config.write().await.selected_folder = None;
|
||||
self.should_reconnect.store(true, Ordering::Relaxed);
|
||||
return ImapActionResult::Failed;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -472,7 +467,7 @@ impl Imap {
|
||||
}
|
||||
}
|
||||
|
||||
ImapActionResult::Success
|
||||
1
|
||||
}
|
||||
|
||||
fn get_config_last_seen_uid<S: AsRef<str>>(&self, context: &Context, folder: S) -> (u32, u32) {
|
||||
@@ -498,16 +493,24 @@ impl Imap {
|
||||
}
|
||||
|
||||
async fn fetch_from_single_folder<S: AsRef<str>>(&self, context: &Context, folder: S) -> usize {
|
||||
match self.select_folder(context, Some(&folder)).await {
|
||||
ImapActionResult::Failed | ImapActionResult::RetryLater => {
|
||||
warn!(
|
||||
context,
|
||||
"Cannot select folder \"{}\" for fetching.",
|
||||
folder.as_ref()
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
ImapActionResult::Success | ImapActionResult::AlreadyDone => {}
|
||||
if !self.is_connected().await {
|
||||
info!(
|
||||
context,
|
||||
"Cannot fetch from \"{}\" - not connected.",
|
||||
folder.as_ref()
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if self.select_folder(context, Some(&folder)).await == 0 {
|
||||
info!(
|
||||
context,
|
||||
"Cannot select folder \"{}\" for fetching.",
|
||||
folder.as_ref()
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// compare last seen UIDVALIDITY against the current one
|
||||
@@ -746,31 +749,25 @@ impl Imap {
|
||||
|
||||
pub fn idle(&self, context: &Context) {
|
||||
task::block_on(async move {
|
||||
if self.config.read().await.selected_folder.is_none() {
|
||||
return;
|
||||
}
|
||||
info!(context, "imap.idle entered");
|
||||
if !self.config.read().await.can_idle {
|
||||
self.fake_idle(context).await;
|
||||
return;
|
||||
}
|
||||
|
||||
info!(context, "setting up handle if needed");
|
||||
self.setup_handle_if_needed(context);
|
||||
|
||||
info!(context, "select watch folder");
|
||||
let watch_folder = self.config.read().await.watch_folder.clone();
|
||||
match self.select_folder(context, watch_folder.as_ref()).await {
|
||||
ImapActionResult::Success | ImapActionResult::AlreadyDone => {}
|
||||
if self.select_folder(context, watch_folder.as_ref()).await == 0 {
|
||||
warn!(context, "IMAP-IDLE not setup.");
|
||||
|
||||
ImapActionResult::Failed | ImapActionResult::RetryLater => {
|
||||
warn!(
|
||||
context,
|
||||
"idle select_folder failed {:?}",
|
||||
watch_folder.as_ref()
|
||||
);
|
||||
self.fake_idle(context).await;
|
||||
return;
|
||||
}
|
||||
self.fake_idle(context).await;
|
||||
return;
|
||||
}
|
||||
|
||||
info!(context, "starting to enter idle loop");
|
||||
let session = self.session.lock().await.take();
|
||||
let timeout = Duration::from_secs(23 * 60);
|
||||
if let Some(session) = session {
|
||||
@@ -783,17 +780,8 @@ impl Imap {
|
||||
let (idle_wait, interrupt) = handle.wait_with_timeout(timeout);
|
||||
*self.interrupt.lock().await = Some(interrupt);
|
||||
|
||||
if *self.skip_next_idle_wait.lock().await {
|
||||
// interrupt_idle has happened before we
|
||||
// provided self.interrupt
|
||||
*self.skip_next_idle_wait.lock().await = false;
|
||||
std::mem::drop(idle_wait);
|
||||
info!(context, "Idle wait was skipped");
|
||||
} else {
|
||||
info!(context, "Idle entering wait-on-remote state");
|
||||
let res = idle_wait.await;
|
||||
info!(context, "Idle finished wait-on-remote: {:?}", res);
|
||||
}
|
||||
let res = idle_wait.await;
|
||||
info!(context, "Idle finished: {:?}", res);
|
||||
match handle.done().await {
|
||||
Ok(session) => {
|
||||
*self.session.lock().await = Some(Session::Secure(session));
|
||||
@@ -811,18 +799,8 @@ impl Imap {
|
||||
let (idle_wait, interrupt) = handle.wait_with_timeout(timeout);
|
||||
*self.interrupt.lock().await = Some(interrupt);
|
||||
|
||||
if *self.skip_next_idle_wait.lock().await {
|
||||
// interrupt_idle has happened before we
|
||||
// provided self.interrupt
|
||||
*self.skip_next_idle_wait.lock().await = false;
|
||||
std::mem::drop(idle_wait);
|
||||
info!(context, "Idle wait was skipped");
|
||||
} else {
|
||||
info!(context, "Idle entering wait-on-remote state");
|
||||
let res = idle_wait.await;
|
||||
info!(context, "Idle finished wait-on-remote: {:?}", res);
|
||||
}
|
||||
|
||||
let res = idle_wait.await;
|
||||
info!(context, "Idle finished: {:?}", res);
|
||||
match handle.done().await {
|
||||
Ok(session) => {
|
||||
*self.session.lock().await = Some(Session::Insecure(session));
|
||||
@@ -890,10 +868,7 @@ impl Imap {
|
||||
|
||||
pub fn interrupt_idle(&self) {
|
||||
task::block_on(async move {
|
||||
if self.interrupt.lock().await.take().is_none() {
|
||||
// idle wait is not running
|
||||
*self.skip_next_idle_wait.lock().await = true;
|
||||
}
|
||||
let _ = self.interrupt.lock().await.take();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1029,16 +1004,14 @@ impl Imap {
|
||||
return Some(ImapActionResult::RetryLater);
|
||||
}
|
||||
}
|
||||
match self.select_folder(context, Some(&folder)).await {
|
||||
ImapActionResult::Success | ImapActionResult::AlreadyDone => None,
|
||||
res => {
|
||||
warn!(
|
||||
context,
|
||||
"Cannot select folder {} for preparing IMAP operation", folder
|
||||
);
|
||||
|
||||
Some(res)
|
||||
}
|
||||
if self.select_folder(context, Some(&folder)).await == 0 {
|
||||
warn!(
|
||||
context,
|
||||
"Cannot select folder {} for preparing IMAP operation", folder
|
||||
);
|
||||
Some(ImapActionResult::RetryLater)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1257,34 +1230,26 @@ impl Imap {
|
||||
task::block_on(async move {
|
||||
info!(context, "emptying folder {}", folder);
|
||||
|
||||
if folder.is_empty() {
|
||||
warn!(context, "cannot perform empty, folder not set");
|
||||
if folder.is_empty() || self.select_folder(context, Some(&folder)).await == 0 {
|
||||
warn!(context, "Cannot select folder '{}' for emptying", folder);
|
||||
return;
|
||||
}
|
||||
match self.select_folder(context, Some(&folder)).await {
|
||||
ImapActionResult::Success | ImapActionResult::AlreadyDone => {
|
||||
if !self
|
||||
.add_flag_finalized_with_set(context, SELECT_ALL, "\\Deleted")
|
||||
.await
|
||||
{
|
||||
warn!(context, "Cannot empty folder {}", folder);
|
||||
} else {
|
||||
// we now trigger expunge to actually delete messages
|
||||
self.config.write().await.selected_folder_needs_expunge = true;
|
||||
if self.select_folder::<String>(context, None).await
|
||||
== ImapActionResult::Success
|
||||
{
|
||||
emit_event!(context, Event::ImapFolderEmptied(folder.to_string()));
|
||||
} else {
|
||||
warn!(
|
||||
context,
|
||||
"could not perform expunge on empty-marked folder {}", folder
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImapActionResult::Failed | ImapActionResult::RetryLater => {
|
||||
warn!(context, "could not select folder {}", folder);
|
||||
|
||||
if !self
|
||||
.add_flag_finalized_with_set(context, SELECT_ALL, "\\Deleted")
|
||||
.await
|
||||
{
|
||||
warn!(context, "Cannot empty folder {}", folder);
|
||||
} else {
|
||||
// we now trigger expunge to actually delete messages
|
||||
self.config.write().await.selected_folder_needs_expunge = true;
|
||||
if self.select_folder::<String>(context, None).await == 0 {
|
||||
warn!(
|
||||
context,
|
||||
"could not perform expunge on empty-marked folder {}", folder
|
||||
);
|
||||
} else {
|
||||
emit_event!(context, Event::ImapFolderEmptied(folder.to_string()));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user