diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 7b9ff845c..3898b5586 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -610,12 +610,8 @@ void dc_interrupt_imap_idle (dc_context_t* context); /** - * Fetch new messages from the MVBOX, if any. - * The MVBOX is a folder on the account where chat messages are moved to. - * The moving is done to not disturb shared accounts that are used by both, - * Delta Chat and a classical MUA. - * - * This function and dc_perform_mvbox_idle() + * Execute pending mvbox-jobs. + * This function and dc_perform_mvbox_fetch() and dc_perform_mvbox_idle() * must be called from the same thread, typically in a loop. * * Example: @@ -623,6 +619,7 @@ void dc_interrupt_imap_idle (dc_context_t* context); * void* mvbox_thread_func(void* context) * { * while (true) { + * dc_perform_mvbox_jobs(context); * dc_perform_mvbox_fetch(context); * dc_perform_mvbox_idle(context); * } @@ -636,13 +633,26 @@ void dc_interrupt_imap_idle (dc_context_t* context); * * // network becomes available again - * // the interrupt causes dc_perform_mvbox_idle() in the thread above - * // to return so that and messages are fetched. + * // to return so that jobs are executed and messages are fetched. * dc_maybe_network(context); * * @memberof dc_context_t * @param context The context as created by dc_context_new(). * @return None. */ +void dc_perform_mvbox_jobs (dc_context_t* context); + + +/** + * Fetch new messages from the MVBOX, if any. + * The MVBOX is a folder on the account where chat messages are moved to. + * The moving is done to not disturb shared accounts that are used by both, + * Delta Chat and a classical MUA. + * + * @memberof dc_context_t + * @param context The context as created by dc_context_new(). + * @return None. + */ void dc_perform_mvbox_fetch (dc_context_t* context); @@ -679,6 +689,39 @@ void dc_perform_mvbox_idle (dc_context_t* context); */ void dc_interrupt_mvbox_idle (dc_context_t* context); +/** + * Execute pending sentbox-jobs. + * This function and dc_perform_sentbox_fetch() and dc_perform_sentbox_idle() + * must be called from the same thread, typically in a loop. + * + * Example: + * + * void* sentbox_thread_func(void* context) + * { + * while (true) { + * dc_perform_sentbox_jobs(context); + * dc_perform_sentbox_fetch(context); + * dc_perform_sentbox_idle(context); + * } + * } + * + * // start sentbox-thread that runs forever + * pthread_t sentbox_thread; + * pthread_create(&sentbox_thread, NULL, sentbox_thread_func, context); + * + * ... program runs ... + * + * // network becomes available again - + * // the interrupt causes dc_perform_sentbox_idle() in the thread above + * // to return so that jobs are executed and messages are fetched. + * dc_maybe_network(context); + * + * @memberof dc_context_t + * @param context The context as created by dc_context_new(). + * @return None. + */ +void dc_perform_sentbox_jobs (dc_context_t* context); + /** * Fetch new messages from the Sent folder, if any. diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 7486e91f1..426507192 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -471,6 +471,18 @@ pub unsafe extern "C" fn dc_perform_mvbox_fetch(context: *mut dc_context_t) { .unwrap_or(()) } +#[no_mangle] +pub unsafe extern "C" fn dc_perform_mvbox_jobs(context: *mut dc_context_t) { + if context.is_null() { + eprintln!("ignoring careless call to dc_perform_mvbox_jobs()"); + return; + } + let ffi_context = &*context; + ffi_context + .with_inner(|ctx| job::perform_mvbox_jobs(ctx)) + .unwrap_or(()) +} + #[no_mangle] pub unsafe extern "C" fn dc_perform_mvbox_idle(context: *mut dc_context_t) { if context.is_null() { @@ -507,6 +519,18 @@ pub unsafe extern "C" fn dc_perform_sentbox_fetch(context: *mut dc_context_t) { .unwrap_or(()) } +#[no_mangle] +pub unsafe extern "C" fn dc_perform_sentbox_jobs(context: *mut dc_context_t) { + if context.is_null() { + eprintln!("ignoring careless call to dc_perform_sentbox_jobs()"); + return; + } + let ffi_context = &*context; + ffi_context + .with_inner(|ctx| job::perform_sentbox_jobs(ctx)) + .unwrap_or(()) +} + #[no_mangle] pub unsafe extern "C" fn dc_perform_sentbox_idle(context: *mut dc_context_t) { if context.is_null() { diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index 22ad3eed9..176f273dc 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -387,7 +387,7 @@ class Account(object): ev = self._evlogger.get_matching("DC_EVENT_INCOMING_MSG") return self.get_message_by_id(ev[2]) - def start_threads(self): + def start_threads(self, mvbox=False, sentbox=False): """ start IMAP/SMTP threads (and configure account if it hasn't happened). :raises: ValueError if 'addr' or 'mail_pw' are not configured. @@ -395,7 +395,7 @@ class Account(object): """ if not self.is_configured(): self.configure() - self._threads.start() + self._threads.start(mvbox=mvbox, sentbox=sentbox) def stop_threads(self, wait=True): """ stop IMAP/SMTP threads. """ @@ -436,10 +436,14 @@ class IOThreads: def is_started(self): return len(self._name2thread) > 0 - def start(self, imap=True, smtp=True): + def start(self, imap=True, smtp=True, mvbox=False, sentbox=False): assert not self.is_started() if imap: - self._start_one_thread("imap", self.imap_thread_run) + self._start_one_thread("inbox", self.imap_thread_run) + if mvbox: + self._start_one_thread("mvbox", self.mvbox_thread_run) + if sentbox: + self._start_one_thread("sentbox", self.sentbox_thread_run) if smtp: self._start_one_thread("smtp", self.smtp_thread_run) @@ -452,17 +456,35 @@ class IOThreads: self._thread_quitflag = True lib.dc_interrupt_imap_idle(self._dc_context) lib.dc_interrupt_smtp_idle(self._dc_context) + lib.dc_interrupt_mvbox_idle(self._dc_context) + lib.dc_interrupt_sentbox_idle(self._dc_context) if wait: for name, thread in self._name2thread.items(): thread.join() def imap_thread_run(self): - self._log_event("py-bindings-info", 0, "IMAP THREAD START") + self._log_event("py-bindings-info", 0, "INBOX THREAD START") while not self._thread_quitflag: lib.dc_perform_imap_jobs(self._dc_context) lib.dc_perform_imap_fetch(self._dc_context) lib.dc_perform_imap_idle(self._dc_context) - self._log_event("py-bindings-info", 0, "IMAP THREAD FINISHED") + self._log_event("py-bindings-info", 0, "INBOX THREAD FINISHED") + + def mvbox_thread_run(self): + self._log_event("py-bindings-info", 0, "MVBOX THREAD START") + while not self._thread_quitflag: + lib.dc_perform_mvbox_jobs(self._dc_context) + lib.dc_perform_mvbox_fetch(self._dc_context) + lib.dc_perform_mvbox_idle(self._dc_context) + self._log_event("py-bindings-info", 0, "MVBOX THREAD FINISHED") + + def sentbox_thread_run(self): + self._log_event("py-bindings-info", 0, "SENTBOX THREAD START") + while not self._thread_quitflag: + lib.dc_perform_sentbox_jobs(self._dc_context) + lib.dc_perform_sentbox_fetch(self._dc_context) + lib.dc_perform_sentbox_idle(self._dc_context) + self._log_event("py-bindings-info", 0, "SENTBOX THREAD FINISHED") def smtp_thread_run(self): self._log_event("py-bindings-info", 0, "SMTP THREAD START") diff --git a/python/tests/conftest.py b/python/tests/conftest.py index fd81c60ff..ba537ca19 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -163,10 +163,10 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig): ac._evlogger.set_timeout(30) return ac, dict(configdict) - def get_online_configuring_account(self): + def get_online_configuring_account(self, mvbox=False, sentbox=False): ac, configdict = self.get_online_config() ac.configure(**configdict) - ac.start_threads() + ac.start_threads(mvbox=mvbox, sentbox=sentbox) return ac def get_two_online_accounts(self): diff --git a/python/tests/test_account.py b/python/tests/test_account.py index e1d2a4f71..c56b4456a 100644 --- a/python/tests/test_account.py +++ b/python/tests/test_account.py @@ -106,7 +106,7 @@ class TestOfflineChat: def chat1(self, ac1): contact1 = ac1.create_contact("some1@hello.com", name="some1") chat = ac1.create_chat_by_contact(contact1) - assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL, chat.id + assert chat.id > const.DC_CHAT_ID_LAST_SPECIAL, chat.id return chat def test_display(self, chat1): @@ -337,14 +337,14 @@ class TestOnlineAccount: def get_chat(self, ac1, ac2): c2 = ac1.create_contact(email=ac2.get_config("addr")) chat = ac1.create_chat_by_contact(c2) - assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL + assert chat.id > const.DC_CHAT_ID_LAST_SPECIAL return chat def test_one_account_send(self, acfactory): ac1 = acfactory.get_online_configuring_account() c2 = ac1.create_contact(email=ac1.get_config("addr")) chat = ac1.create_chat_by_contact(c2) - assert chat.id >= const.DC_CHAT_ID_LAST_SPECIAL + assert chat.id > const.DC_CHAT_ID_LAST_SPECIAL wait_successful_IMAP_SMTP_connection(ac1) wait_configuration_progress(ac1, 1000) @@ -365,6 +365,16 @@ class TestOnlineAccount: msg_in = ac2.get_message_by_id(msg_out.id) assert msg_in.text == "message1" + def test_mvbox_sentbox_threads(self, acfactory): + 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) + chat = self.get_chat(ac1, ac2) + chat.send_text("message1") + ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED") + assert ev[2] > const.DC_CHAT_ID_LAST_SPECIAL + def test_forward_messages(self, acfactory): ac1, ac2 = acfactory.get_two_online_accounts() chat = self.get_chat(ac1, ac2) @@ -430,8 +440,8 @@ class TestOnlineAccount: ac2.mark_seen_messages([msg_in]) lp.step("1") ev = ac1._evlogger.get_matching("DC_EVENT_MSG_READ") - assert ev[1] >= const.DC_CHAT_ID_LAST_SPECIAL - assert ev[2] >= const.DC_MSG_ID_LAST_SPECIAL + assert ev[1] > const.DC_CHAT_ID_LAST_SPECIAL + assert ev[2] > const.DC_MSG_ID_LAST_SPECIAL lp.step("2") assert msg_out.is_out_mdn_received() diff --git a/src/job.rs b/src/job.rs index d026eb243..1cf074fd9 100644 --- a/src/job.rs +++ b/src/job.rs @@ -761,6 +761,14 @@ pub fn perform_imap_jobs(context: &Context) { info!(context, "dc_perform_imap_jobs ended.",); } +pub fn perform_mvbox_jobs(context: &Context) { + info!(context, "dc_perform_mbox_jobs EMPTY (for now).",); +} + +pub fn perform_sentbox_jobs(context: &Context) { + info!(context, "dc_perform_sentbox_jobs EMPTY (for now).",); +} + fn job_perform(context: &Context, thread: Thread, probe_network: bool) { let query = if !probe_network { // processing for first-try and after backoff-timeouts: