diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 660a90e02..8d2603129 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -1621,7 +1621,7 @@ pub unsafe extern "C" fn dc_stop_ongoing_process(context: *mut dc_context_t) { } let ffi_context = &*context; ffi_context - .with_inner(|ctx| configure::dc_stop_ongoing_process(ctx)) + .with_inner(|ctx| ctx.stop_ongoing()) .ok(); } diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index e1501a5f5..dfcaf4515 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -411,6 +411,9 @@ class Account(object): raise ValueError("could not join group") return Chat(self, chat_id) + def stop_ongoing(self): + lib.dc_stop_ongoing_process(self._dc_context) + # # meta API for start/stop and event based processing # @@ -432,7 +435,7 @@ class Account(object): def stop_threads(self, wait=True): """ stop IMAP/SMTP threads. """ - lib.dc_stop_ongoing_process(self._dc_context) + self.stop_ongoing() self._threads.stop(wait=wait) def shutdown(self, wait=True): diff --git a/python/tests/test_account.py b/python/tests/test_account.py index d91b9b02e..05f41ae72 100644 --- a/python/tests/test_account.py +++ b/python/tests/test_account.py @@ -350,6 +350,12 @@ class TestOnlineAccount: assert chat.id > const.DC_CHAT_ID_LAST_SPECIAL return chat + def test_configure_canceled(self, acfactory): + ac1 = acfactory.get_online_configuring_account() + wait_configuration_progress(ac1, 200) + ac1.stop_ongoing() + wait_configuration_progress(ac1, 0, 0) + def test_export_import_self_keys(self, acfactory, tmpdir): ac1, ac2 = acfactory.get_two_online_accounts() dir = tmpdir.mkdir("exportdir") diff --git a/src/configure/mod.rs b/src/configure/mod.rs index 6116babdc..c5afda852 100644 --- a/src/configure/mod.rs +++ b/src/configure/mod.rs @@ -28,7 +28,7 @@ macro_rules! progress { // connect pub unsafe fn configure(context: &Context) { - if dc_has_ongoing(context) { + if context.has_ongoing() { warn!(context, "There is already another ongoing process running.",); return; } @@ -53,7 +53,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) { let mut ongoing_allocated_here = false; let mut param_autoconfig: Option = None; - if dc_alloc_ongoing(context) { + if context.alloc_ongoing() { ongoing_allocated_here = true; if !context.sql.is_open() { error!(context, "Cannot configure, database not opened.",); @@ -74,9 +74,6 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) { context.smtp.clone().lock().unwrap().disconnect(); info!(context, "Configure ...",); - let s_a = context.running_state.clone(); - let s = s_a.read().unwrap(); - // Variables that are shared between steps: let mut param = LoginParam::from_database(context, ""); // need all vars here to be mutable because rust thinks the same step could be called multiple times @@ -88,7 +85,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) { const STEP_3_INDEX: u8 = 13; let mut step_counter: u8 = 0; - while !s.shall_stop_ongoing { + while !context.shall_stop_ongoing() { step_counter = step_counter + 1; let success = match step_counter { @@ -358,7 +355,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) { break; } // probe STARTTLS/993 - if s.shall_stop_ongoing { + if context.shall_stop_ongoing() { ok_to_continue8 = false; break; } @@ -372,7 +369,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) { break; } // probe STARTTLS/143 - if s.shall_stop_ongoing { + if context.shall_stop_ongoing() { ok_to_continue8 = false; break; } @@ -389,7 +386,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) { break; } // next probe round with only the localpart of the email-address as the loginname - if s.shall_stop_ongoing { + if context.shall_stop_ongoing() { ok_to_continue8 = false; break; } @@ -426,7 +423,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) { { if param_autoconfig.is_some() { success = false; - } else if s.shall_stop_ongoing { + } else if context.shall_stop_ongoing() { success = false; } else { progress!(context, 850); @@ -442,7 +439,7 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) { .unwrap() .connect(context, ¶m) { - if s.shall_stop_ongoing { + if context.shall_stop_ongoing() { success = false; } else { progress!(context, 860); @@ -553,47 +550,12 @@ pub unsafe fn dc_job_do_DC_JOB_CONFIGURE_IMAP(context: &Context) { } */ if ongoing_allocated_here { - dc_free_ongoing(context); + context.free_ongoing(); } progress!(context, if success { 1000 } else { 0 }); } -/******************************************************************************* - * Ongoing process allocation/free/check - ******************************************************************************/ - -pub fn dc_alloc_ongoing(context: &Context) -> bool { - if dc_has_ongoing(context) { - warn!(context, "There is already another ongoing process running.",); - - false - } else { - let s_a = context.running_state.clone(); - let mut s = s_a.write().unwrap(); - - s.ongoing_running = true; - s.shall_stop_ongoing = false; - - true - } -} - -pub fn dc_free_ongoing(context: &Context) { - let s_a = context.running_state.clone(); - let mut s = s_a.write().unwrap(); - - s.ongoing_running = false; - s.shall_stop_ongoing = true; -} - -fn dc_has_ongoing(context: &Context) -> bool { - let s_a = context.running_state.clone(); - let s = s_a.read().unwrap(); - - s.ongoing_running || !s.shall_stop_ongoing -} - /******************************************************************************* * Connect to configured account ******************************************************************************/ @@ -620,19 +582,6 @@ pub fn dc_connect_to_configured_imap(context: &Context, imap: &Imap) -> libc::c_ * Configure a Context ******************************************************************************/ -/// Signal an ongoing process to stop. -pub fn dc_stop_ongoing_process(context: &Context) { - let s_a = context.running_state.clone(); - let mut s = s_a.write().unwrap(); - - if s.ongoing_running && !s.shall_stop_ongoing { - info!(context, "Signaling the ongoing process to stop ASAP.",); - s.shall_stop_ongoing = true; - } else { - info!(context, "No ongoing process to stop.",); - }; -} - pub fn read_autoconf_file(context: &Context, url: &str) -> Option { info!(context, "Testing {} ...", url); diff --git a/src/context.rs b/src/context.rs index cd15a4c4a..82ec37c07 100644 --- a/src/context.rs +++ b/src/context.rs @@ -68,7 +68,7 @@ pub struct Context { #[derive(Debug, PartialEq, Eq)] pub struct RunningState { pub ongoing_running: bool, - pub shall_stop_ongoing: bool, + shall_stop_ongoing: bool, } /// Return some info about deltachat-core @@ -220,6 +220,62 @@ impl Context { (*self.cb)(self, event) } + /******************************************************************************* + * Ongoing process allocation/free/check + ******************************************************************************/ + + pub fn alloc_ongoing(&self) -> bool { + if self.has_ongoing() { + warn!(self, "There is already another ongoing process running.",); + + false + } else { + let s_a = self.running_state.clone(); + let mut s = s_a.write().unwrap(); + + s.ongoing_running = true; + s.shall_stop_ongoing = false; + + true + } + } + + pub fn free_ongoing(&self) { + let s_a = self.running_state.clone(); + let mut s = s_a.write().unwrap(); + + s.ongoing_running = false; + s.shall_stop_ongoing = true; + } + + pub fn has_ongoing(&self) -> bool { + let s_a = self.running_state.clone(); + let s = s_a.read().unwrap(); + + s.ongoing_running || !s.shall_stop_ongoing + } + + /// Signal an ongoing process to stop. + pub fn stop_ongoing(&self) { + let s_a = self.running_state.clone(); + let mut s = s_a.write().unwrap(); + + if s.ongoing_running && !s.shall_stop_ongoing { + info!(self, "Signaling the ongoing process to stop ASAP.",); + s.shall_stop_ongoing = true; + } else { + info!(self, "No ongoing process to stop.",); + }; + } + + pub fn shall_stop_ongoing(&self) -> bool { + self.running_state.clone().read().unwrap().shall_stop_ongoing + } + + /******************************************************************************* + * UI chat/message related API + ******************************************************************************/ + pub fn get_info(&self) -> HashMap<&'static str, String> { let unset = "0"; let l = LoginParam::from_database(self, ""); diff --git a/src/imex.rs b/src/imex.rs index 563610803..375c977a1 100644 --- a/src/imex.rs +++ b/src/imex.rs @@ -110,32 +110,26 @@ pub fn has_backup(context: &Context, dir_name: impl AsRef) -> Result Result { + ensure!(context.alloc_ongoing(), "could not allocate ongoing"); + let res = do_initiate_key_transfer(context); + context.free_ongoing(); + res +} + +fn do_initiate_key_transfer(context: &Context) -> Result { let mut msg: Message; - ensure!(dc_alloc_ongoing(context), "could not allocate ongoing"); let setup_code = create_setup_code(context); /* this may require a keypair to be created. this may take a second ... */ - if !context - .running_state - .clone() - .read() - .unwrap() - .shall_stop_ongoing - { - if let Ok(ref setup_file_content) = render_setup_file(context, &setup_code) { + ensure!(!context.shall_stop_ongoing(), "canceled"); + let setup_file_content = render_setup_file(context, &setup_code)?; /* encrypting may also take a while ... */ - if !context - .running_state - .clone() - .read() - .unwrap() - .shall_stop_ongoing - { + ensure!(!context.shall_stop_ongoing(), "canceled"); let setup_file_name = context.new_blob_file( "autocrypt-setup-message.html", setup_file_content.as_bytes(), )?; - { - if let Ok(chat_id) = chat::create_by_contact_id(context, 1) { + + let chat_id = chat::create_by_contact_id(context, 1)?; msg = Message::default(); msg.type_0 = Viewtype::File; msg.param.set(Param::File, setup_file_name); @@ -146,25 +140,10 @@ pub fn initiate_key_transfer(context: &Context) -> Result { msg.param .set_int(Param::ForcePlaintext, DC_FP_NO_AUTOCRYPT_HEADER); - if !context - .running_state - .clone() - .read() - .unwrap() - .shall_stop_ongoing - { - if let Ok(msg_id) = chat::send_msg(context, chat_id, &mut msg) { + ensure!(!context.shall_stop_ongoing(), "canceled"); + let msg_id = chat::send_msg(context, chat_id, &mut msg)?; info!(context, "Wait for setup message being sent ...",); - loop { - if context - .running_state - .clone() - .read() - .unwrap() - .shall_stop_ongoing - { - break; - } + while !context.shall_stop_ongoing() { std::thread::sleep(std::time::Duration::from_secs(1)); if let Ok(msg) = Message::load_from_db(context, msg_id) { if msg.is_sent() { @@ -173,15 +152,6 @@ pub fn initiate_key_transfer(context: &Context) -> Result { } } } - } - } - } - } - } - } - } - dc_free_ongoing(context); - Ok(setup_code) } @@ -420,7 +390,7 @@ pub fn normalize_setup_code(s: &str) -> String { #[allow(non_snake_case)] pub fn job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) -> Result<()> { - ensure!(dc_alloc_ongoing(context), "could not allocate ongoing"); + ensure!(context.alloc_ongoing(), "could not allocate ongoing"); let what: Option = job.param.get_int(Param::Cmd).and_then(ImexMode::from_i32); let param = job.param.get(Param::Arg).unwrap_or_default(); @@ -432,7 +402,7 @@ pub fn job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) -> Result<()> { if what == Some(ImexMode::ExportBackup) || what == Some(ImexMode::ExportSelfKeys) { // before we export anything, make sure the private key exists if e2ee::ensure_secret_key_exists(context).is_err() { - dc_free_ongoing(context); + context.free_ongoing(); bail!("Cannot create private key or private key not available."); } else { dc_create_folder(context, ¶m); @@ -448,7 +418,7 @@ pub fn job_do_DC_JOB_IMEX_IMAP(context: &Context, job: &Job) -> Result<()> { bail!("unknown IMEX type"); } }; - dc_free_ongoing(context); + context.free_ongoing(); match success { Ok(()) => { info!(context, "IMEX successfully completed"); @@ -514,15 +484,7 @@ fn import_backup(context: &Context, backup_to_import: impl AsRef) -> Resul |files| { for (processed_files_cnt, file) in files.enumerate() { let (file_name, file_blob) = file?; - if context - .running_state - .clone() - .read() - .unwrap() - .shall_stop_ongoing - { - bail!("received stop signal"); - } + ensure!(!context.shall_stop_ongoing(), "received stop signal"); let mut permille = processed_files_cnt * 1000 / total_files_cnt; if permille < 10 { permille = 10 @@ -636,15 +598,7 @@ fn add_files_to_export(context: &Context, dest_path_filename: &PathBuf) -> Resul let mut processed_files_cnt = 0; for entry in dir_handle { let entry = entry?; - if context - .running_state - .clone() - .read() - .unwrap() - .shall_stop_ongoing - { - bail!("canceled during export-files"); - } + ensure!(!context.shall_stop_ongoing(), "canceled during export-files"); processed_files_cnt += 1; let permille = max(min(processed_files_cnt * 1000 / total_files_cnt, 990), 10); context.call_cb(Event::ImexProgress(permille)); diff --git a/src/securejoin.rs b/src/securejoin.rs index 4aa0cf100..5e76a61b9 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -3,7 +3,6 @@ use percent_encoding::{utf8_percent_encode, AsciiSet, NON_ALPHANUMERIC}; use crate::aheader::EncryptPreference; use crate::chat::{self, Chat}; use crate::config::*; -use crate::configure::*; use crate::constants::*; use crate::contact::*; use crate::context::Context; @@ -157,7 +156,7 @@ pub fn dc_join_securejoin(context: &Context, qr: &str) -> u32 { bob.qr_scan = None; if ongoing_allocated { - dc_free_ongoing(context); + context.free_ongoing(); } ret_chat_id as u32 }; @@ -170,7 +169,7 @@ pub fn dc_join_securejoin(context: &Context, qr: &str) -> u32 { info!(context, "Requesting secure-join ...",); ensure_secret_key_exists(context).ok(); - if !dc_alloc_ongoing(context) { + if !context.alloc_ongoing() { return cleanup(&context, contact_chat_id, false, join_vg); } let qr_scan = check_qr(context, &qr); @@ -184,7 +183,7 @@ pub fn dc_join_securejoin(context: &Context, qr: &str) -> u32 { error!(context, "Unknown contact.",); return cleanup(&context, contact_chat_id, true, join_vg); } - if check_exit(context) { + if context.shall_stop_ongoing() { return cleanup(&context, contact_chat_id, true, join_vg); } join_vg = qr_scan.get_state() == LotState::QrAskVerifyGroup; @@ -240,21 +239,12 @@ pub fn dc_join_securejoin(context: &Context, qr: &str) -> u32 { } // Bob -> Alice - while !check_exit(&context) { + while !context.shall_stop_ongoing() { std::thread::sleep(std::time::Duration::new(0, 3_000_000)); } cleanup(&context, contact_chat_id, true, join_vg) } -fn check_exit(context: &Context) -> bool { - context - .running_state - .clone() - .read() - .unwrap() - .shall_stop_ongoing -} - fn send_handshake_msg( context: &Context, contact_chat_id: u32, @@ -636,7 +626,7 @@ pub fn handle_securejoin_handshake( fn end_bobs_joining(context: &Context, status: libc::c_int) { context.bob.write().unwrap().status = status; - dc_stop_ongoing_process(context); + context.stop_ongoing(); } fn secure_connection_established(context: &Context, contact_chat_id: u32) {